Twiggery 脚本语言
Twiggery 脚本语言
引言
Twiggery 是一种小巧但有用的脚本语言。一个完整的 Twiggery 系统由一个编译器和一个虚拟机组成。而且,它比任何其他脚本语言都更容易将脚本与宿主程序(C++、Java、C# 等)结合,最精简的 TVM 实现源代码不超过 25KB。
背景
编写游戏是很有趣的!原因很简单,因为在游戏中你可以编写各种不同类型的子系统,无论是渲染器、声音系统、AI 还是游戏逻辑代码;在所有这些类型的编程中,你都可以解决各种挑战。在很多情况下,使用脚本是将游戏代码与主引擎隔离的理想方式。在 PC 平台、MAC 和游戏机上,有许多强大且成熟的脚本语言供开发人员选择,如 Lua、Python、Ruby 等。但在 J2ME 手机等移动平台上,开发人员没有选择,这就是为什么创建了这种优秀的脚本语言。你将了解 Twiggery 脚本语言是什么以及它是如何工作的;Twiggery 的最佳用途是将其用作手机脚本语言;当然,你还将了解一些编译器理论。你甚至可以研究一个完整的 Twiggery 虚拟机是如何开发的!如果你热爱编程,特别是编写游戏,我相信你会乐于了解更多关于这种小巧的脚本语言。祝你玩得开心!
如何使用 Twiggery 编程
基本语法
关键词
Twiggery 中只有 17 个关键字,如下所示
function
if
elseif
else
for
to
step
while
do
break
continue
return
和
或
not
true
false
每个单词的含义将在后面的部分介绍。
Twiggery 是一种区分大小写的语言,也就是说,“function
” 是一个关键字,表示脚本函数的头部,而 Function
或 FUNCTION
在语法上与“function
”不同。
运算符
Twiggery 中有 15 个运算符,如下所示
类型 |
词汇 |
描述 |
示例 |
赋值 |
= |
给变量赋值 |
pi = 3.14: pi 是 3.14 |
数值 计算 |
+ |
将一对值相加 |
r = 2 + 3: r 是 5 |
- |
用一个值减去另一个值 |
r = 5 – 2: r 是 3 |
|
* |
将两个值相乘 |
r = 3 * 7: r 是 21 |
|
/ |
将一个值除以另一个值 |
r = 18 / 3: r 是 6 |
|
% |
取模运算 |
r = 10 % 3: r 是 1 |
|
数值 比较 |
< |
小于 |
c = 1 < 2: c 是 true |
> |
大于 |
c = 1 > 2: c 是 false |
|
<= |
小于或等于 |
c = 1 <= 2: c 是 true |
|
>= |
大于或等于 |
c = 1 >= 1: c 是 true |
|
== |
等于 |
c = 4 == 5: c 是 false |
|
~= |
不等于 |
c = 4 ~= 5: c 是 true |
|
布尔值 运算 |
和 |
“与”逻辑 |
参见“运算符” |
或 |
“或”逻辑 |
||
not |
“非”逻辑 |
关键字“false
”表示一个表达式的结果为 false
,它等同于 C 语言中的数字零;而“true
”表示 true
或非零。“false
”或“true
”是数字比较或布尔运算的结果值。
关键字“and
”、“or
”和“not
”也是运算符。请参阅 Twiggery 运算 优先级 表。
信号强度 | 操作 (Operation) |
1 | * / % |
2 | + - |
3 | < > <= >= ~= == |
4 | not |
5 | and or |
6 | = |
级别 1
的优先级最高,级别 6
的优先级最低。级别高的运算先于级别低的运算执行。公式从左到右处理;同一级别的运算以相同方式处理。括号‘(‘和‘)’成对使用,以处理它们之间的公式;例如 5 * (1 + 2) % 4 返回值 3。
注释
编译器除了跳过注释外,不会做任何事情。但这并不意味着注释不重要,注释在代码注释中起着重要作用,可以为代码阅读者提供很多信息。
Twiggery 注释以字符 ‘ 开头,以行尾结束。‘ 之后的所有内容都将被跳过。如下所示
' Welcome to Twiggery Scripting Language. This line is a comment. function main() { output("Hello, Twiggery!"); ' Output a string. This is also a comment. }
变量与运算
数据类型
Twiggery 中有三种数据类型:数字、布尔和字符串。
数字是一种基本数据类型。Twiggery 只有一个数字类型;它以 32 位存储,并始终定义为 IEEE754 标准浮点类型。其范围是 3.402823e+38 ~ 1.401298e-45。
布尔可以被赋值为“true
”或“false
”这两个值之一。在 Twiggery 中,布尔在 32 位中存储和处理方式与数字类似;数值零表示“false
”,所有非零值表示“true
”。
字符串是通过不同 TVM 编码语言中的本机字符串类型实现的。字符串只能用作函数的参数。例如:output("Twiggery");
。
你可以在 Twiggery 中进行数字和布尔计算、比较和赋值,如下所示
' assignment pi = 3.14; ' remote function call, input a value into 'r' input ( r ); ' calculation s = pi * r * r; ' compare two value and assignment the result to a boolean t = 1 < 2; ' t is true f = 1 == 2; ' f is false ' two simple variables a = 1; b = 0; ' boolean operation between a numeric and a boolean const b1 = a or false; ' b1 is true b2 = not b and true; ' b2 is true
脚本中的每个变量都是全局的,也就是说,你可以在任何地方、任何时间访问任何变量。变量类型声明不是必需的,因为 Twiggery 是一种弱类型语言,如果你想使用一个变量,只需写下来即可。
数字计算
编程语言的主要目的是数据处理,而数据处理的主要目的是数值计算。
有五种数字计算运算。这很简单,所以更多的解释会让你感到厌烦。你可以像下面一样使用 +、-、*、/、% 作为加、减、乘、除和取模运算
s = v0 * t + (1 / 2) * 9.8 * t * t; m = 10 % 3; ' m is 1
数字比较
这些运算比较两个数字并返回布尔值,它们根据比较公式是否相等来输出“true
”或“false
”。任何比较只能产生“true
”或“false
”之一。Twiggery 数字比较如下所示。
比较 | 示例 | 描述(结果为 true) |
< | ex1 < ex2 | ex1 小于 ex2 |
> | ex1 > ex2 | ex1 大于 ex2 |
<= | ex1 <= ex2 | ex1 小于或等于 ex2 |
>= | ex1 >= ex2 | ex1 大于或等于 ex2 |
== | ex1 == ex2 | ex1 等于 ex2 |
~= | ex1 ~= ex2 | ex1 不等于 ex2 |
布尔运算
布尔是一种特殊的表示数字的方式,所以 支持布尔 和数字比较。“true
”被当作 非 零 处理,“false
”被当作零处理。
控制 Twig 执行会使用布尔值作为其条件。稍后将显示更多详细信息。
字符串
Twiggery 最初设计为一种 JavaME 嵌入式脚本语言,仅用于简单的逻辑控制和数字处理。因此,字符串操作不是它的强项。
在某些用法中,字符串类型的值只能作为远程函数参数传递。例如,将字符串“Hello
”传递给标准输出函数“output
”,字符串将显示在标准控制台上。
函数
如你所见,在当前版本的 Twiggery 中,每个脚本源文件只有一个函数。与其他复杂的编程语言不同,Twiggery 是一种小巧且易于嵌入的脚本,因此其设计理念是“用更少的代码做更多的事”。
每个 Twiggery 脚本源文件只包含一个无参数函数。执行流程从函数的第一行开始。多函数调用和进入点函数参数支持可能在后续版本中可选。
执行结构
我们称 Twiggery 为脚本语言而不是公式处理器,因为它像其他编程语言一样有三种执行结构:顺序、条件和循环流。在本节中,你将学习如何使用它们。
与 C/C++/Java/C# 等类似,Twiggery 的作用域由花括号 {} 的位置决定,语句以分号; 结束。花括号必须成对使用。允许但 Constructors(不推荐)在一行中编写多个语句。
顺序流
顺序流语句一行接一行编写,并按编写顺序执行。例如,测试如下代码。
function main() { ' Now let's do some calculating. pi = 3.1415926; ' Step 1: Assign p to variable pi r = input(); ' Step 2: Input a value to variable r s = pi * r * r; ' Step 3: Calculate area of a circle output( s); ' Step 4: Output the result }
如你所见,每个 Twiggery 脚本源文件只包含一个“function
”。在步骤pi
的变量;在步骤 2 中,我们调用一个标准的远程函数“input
”从用户输入(控制台或输入框…)获取一个值并将其设置为变量 r
;在步骤 3 中,我们进行一个你知道的计算:S =pR2;最后,结果区域“s
”在最后一步 4 中显示。
开始使用 Twiggery 很容易,不是吗?
if-elseif-else
标准的“if
” twig 遵循如下语法
if( expression) { twig_body; }
如果公式表达式的结果为“true
”或非零,则执行子 twig“twig_body
”;否则,它将跳转到此“if
”块后面的下一个 twig。
twig body 包含一个或多个子 twig。例如
n = input(); if( n % 400 == 0 or ( n % 4 == 0 and n % 100 ~= 0)) { output("leap year"); }
Twiggery 中没有“最佳路径”。这意味着“or
”后面的公式会每次都计算。因此,即使在本示例中“ n % 400 ==true
;“n % 100 ~=
”也将在任何情况下被处理。
允许在“if
” twig 后直接追加最多一个“else
” twig。此外,“if-elseif
…else
”结构在复杂的条件 twig 中非常有用。“elseif
”和“else
”在“if-elseif
…else
”结构中都是可选的。如下所示
c = input(); ' Input a variable. if( c == 1) { ' If the variable "c" equals to "1"... output("Uno"); ' Call a system function to output something. } elseif(c == 2) { output("Dos"); } elseif(c == 3) { output("Tres"); } else { output(c); }
在此示例中,第二个条件“c == 2
”仅在“c
”不等于 1
时确定,第三个条件类似。最后一个“output(c)
”仅在“c
”不等于 1
、2
和 3
时执行。
for, while 和 do-while 循环
“if-elseif
…else
” twig 只能为 Twiggery 提供跳过能力。在本节中,你将学习一些有用的具有重复能力的 twig。
有三种重复 twig:“for-to-step
”、“while
”和“do-while
”。
“for
” twig 被视为固定步长循环。如下所示
n = 1; ' Initialize "n" with value "1". e = 10; for( i = 1 to e step 1) { ' A loop. n = n * i; } output ( n ); ' Output n!
在此示例中,变量“i
" 从 1
计数到 e
(即 10
),循环步长为 1。这意味着 twig body“n = n * I
”被处理 10 次。循环步长的默认值为 1,因此此 twig 头部也可以写成“for(i = 1 to e)
”。
有时我们不知道循环需要多少步。为此,动态变量循环是必要的。Twiggery 中有两种不固定循环:“while
”和“do-while
”循环。
“while
”和“do-while
” twig 实际上是条件循环。它们会在“if
” twig 中一旦条件为 true
就跳转到 twig body 执行;并且在“while
”和“do-while
”中,twig body 会一遍又一遍地执行,直到循环条件不再为真;这两个循环的区别在于,如果条件为 false
,则“while
” twig body 不会被处理,但无论条件值如何,“do-while
” twig body 至少会运行一次,这是因为在“while
”中是在 twig body 之前判断循环条件,而在“do-while
”中是在 twig body 之后判断。例如
a = 0; while( a <= 5) { ' Execute inside if loop condition is true output ( a ); a = a + 1; } b = 0; do { ' Execute inside at least once whether loop condition is true or false output(b); b = b + 1; } while(b <= 5)
break, continue 和 return
Twiggery 中的“break
”用于中断当前循环并继续执行其后的程序。它与 C 编程语言相同。它总是通过外部条件触发(通常通过“if
” twig 测试)来中断当前循环。“break
”可以用于“for
”、“while
”和“do-while
” twig。
num = input(); count = num / 2; while( count > 0) { if( num % count == 0) { output( count, " is the largest factor of ", num); break; } count = count - 1; }
此脚本将找出给定数字“num
”的最大因子。我们迭代所有可能的数字,并使用“count
”作为循环变量,按降序拒绝所有不能被“num
”整除的数字。然后,第一个能被“num
”整除的数字就是我们要找的,如果我们找到了,就不需要再执行循环了,我们只需使用“break
”退出循环。
Twiggery 中的“continue
”与其他编程语言没有区别。它可以用于“for
”、“while
”和“do-while
”,类似于“break
”。“while
”和“do-while
”是条件循环,“for
”是迭代循环。是否执行循环的下一个周期在遇到“continue
”时也取决于循环类型的需求;当循环不满足条件时,它将正常终止。如下所示
valid = false; count = 3; while( count > 0) { it = input(); if(it == 123) { valid = true; break; } if(not valid) { output("Invalid"); count = count - 1; continue; } else { break; } }
有时我们想完全跳出整个函数并退出脚本执行,“return
”就是为此而设计的;“return
”仅用于执行此操作,但不能将值返回给外部调用者。如下所示
for( i = 0 to 10) { n = input(); if( i % n ~= 0) { return; } }
如果“i % n ~= 0
”为 true
,程序将毫无疑问地直接终止。
输入和输出
如果没有输入和输出,我们无法使用编程语言进行任何操作,只能进行内部处理。在 Twiggery 中,有一个标准的输入函数和一个输出 函数 。“input
” 函数不接受任何参数并返回一个数字值;“output
”函数至少接受一个数字或字符串格式的参数,并将其输出到标准流(Windows 上的消息框、控制台上的行打印等)。
历史
- 2010年8月12日 - 版本:0.5.2.44
- 为 C# TVM 添加
try
-catch
以直接加载和运行字节码 - 为脚本文本框添加拖放支持
- Java TVM 中的字节码加载错误修复
- 为 C# TVM 添加
- 2010年7月28日 - 版本:0.5.1.43
- 音频插件错误修复
- 为 Core 插件添加数组支持
- 为所有 TVM 添加动态多参数支持
- 在普通公式中接受函数调用
- 负号错误修复
- 2010年7月3日 - 版本:0.4.2.38
- 脚本内部异常错误修复
- 2010年6月24日 - 版本:0.4.1.36
- 添加插件加载过程窗口
- 关联“
.twg
”和“.tad
”文件 - 添加新的 Core 插件
- 为 CodeLeaf 添加自动补全提示
- 条件表达式解析错误修复
- 为 Graphics 插件添加一些新界面
- 2010年6月18日 - 版本:0.4.0.30
- 修改 Graphics 插件:将画布图像放在
PictureBox
中,而不是直接放在窗体上 - 添加关键字 '
do
' 和 'do-while
' twig 支持 - 添加负号支持
- 重新配置 Graphics 插件
- 修改 Graphics 插件:将画布图像放在
- 2010年6月16日 - 版本:0.3.5.28
- 在 RAM 框中显示堆单元使用情况
- 在标准函数 'input' 和 'output' 中接受多参数
- 如果两个操作数的寻址模式都是 AM_STK,则交换它们
- TASM 列表框项数错误修复
- 添加字符串类型插件函数参数支持
- 修改 Graphics 插件:创建 Canvas,绘制 Sprites,获取
KeyDown
等 - 修改 Audio 插件:C# 源代码格式插件,播放 *.wav 文件
- 修改 IO 插件:打开、关闭、写入、读取文件
- 如果获取操作数失败,将参数推回堆栈
- '
if-elseif-else
' 块编译错误修复 - 将默认的 '
for
' 循环步长参数值设为 1 - 将返回值获取为格式 '
a = input();
' - 重新配置异常消息
- 添加使用插件的新示例脚本
- 在函数参数中将 '
true
'、'false
' 转义为 '1
'、'0
'。
- 2010年6月12日 - 版本:0.2.3.25
- 添加已编译程序集 DLL 和 C# 源代码插件支持
- 在 TVM 执行中使用 '
v is t
' 而不是 'v.GetType() == typeof(t)
'。
- 2010年6月9日 - 版本:0.1.2.24
- '
if-elseif-else
' 跳转指令编译错误修复
- '
- 2010年2月24日 - 版本:0.1.0.20
- 首次发布版本。完整的编译器和虚拟机