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- 首次发布版本。完整的编译器和虚拟机
 




