TL (Tiny Language) 是一种由AI设计和制作为特定虚拟机设计的极简命令式编程语言。它支持基本的变量声明、赋值、算术运算、条件控制和循环结构。本文档旨在详细说明TL语言的语法规则。
TL语言支持单行注释。注释以 // 开始,直至该行结束。
// 这是一个单行注释
VAR x : INT; // 变量声明也可以有行尾注释标识符用于命名变量。
- 规则: 以字母 (a-z, A-Z) 或下划线 (
_) 开头,后跟任意数量的字母、数字 (0-9) 或下划线。 - 示例:
myVariable,counter,_tempVal,value1
TL语言目前仅支持整数类型。
- 规则: 整数可以包含一个可选的负号 (
-),后跟一位或多位数字。 - 示例:
123,0,-42,10000
TL语言有以下保留关键字,不能用作标识符:
VAR: 用于声明变量。INT: 指定变量类型为整数。IF: 条件语句的开始。ELSE:IF语句的可选分支。WHILE: 循环语句的开始。IN: 用于从标准输入读取整数。OUT: 用于向标准输出打印表达式的值。
空白符(空格、制表符、换行符)用于分隔语法单元,但通常不影响程序逻辑(除非在字符串字面量中,但TL目前不支持字符串字面量)。良好的缩进和空白使用可以提高代码可读性。
一个完整的TL程序由一个语句列表 (statement_list) 和一个文件结束符 (EOF) 构成。
- 语法:
statement_list EOF
语句列表包含零个或多个语句。它可以是程序的主体,也可以是 IF 或 WHILE 语句块内部的内容。
- 语法:
statement*(表示零个或多个statement) - 示例:
// 一个包含多个语句的语句列表 VAR x : INT; x = 10; OUT(x);
// 一个空的语句列表 (例如在一个IF块中) IF (x > 10) { // 空块 }
一个语句是语言中一个完整的执行单元。TL支持以下类型的语句,除 IF 和 WHILE 语句外,其他语句通常以分号 (;) 结尾。
- 语法:
statement: declaration ';' | assignment ';' | input_statement ';' | output_statement ';' | if_statement | while_statement ;
声明一个或多个指定类型的变量。在当前版本的TL中,所有变量都是全局整数类型。
- 语法:
K_VAR IDENTIFIER ':' K_INT_TYPE ; - 示例:
VAR count : INT; VAR tempValue : INT;
将一个表达式的值赋给一个已声明的变量。
- 语法:
IDENTIFIER ASSIGN expression ; - 示例:
count = 0; sum = value1 + value2; count = count + 1;
从标准输入读取一个整数,并将其赋给一个变量。
- 语法:
IDENTIFIER ASSIGN K_IN LPAREN RPAREN ; - 示例:
VAR userInput : INT; userInput = IN();
计算一个表达式的值,并将其输出到标准输出。
- 语法:
K_OUT LPAREN expression RPAREN ; - 示例:
VAR result : INT; result = 10 * 5; OUT(result); // 输出 50 OUT(result + 5); // 输出 55 OUT(100); // 直接输出常量
根据条件的真假执行不同的语句块。ELSE 部分是可选的。
- 语法:
K_IF LPAREN condition RPAREN LBRACE statement_list RBRACE (K_ELSE LBRACE statement_list RBRACE)? ; - 示例 (有 ELSE):
VAR score : INT; score = IN(); IF (score >= 60) { OUT(1); // 代表 "及格" } ELSE { OUT(0); // 代表 "不及格" }
- 示例 (无 ELSE):
VAR positiveFlag : INT; positiveFlag = 0; IF (userInput > 0) { positiveFlag = 1; OUT(userInput); } // 如果 userInput <= 0,则不执行块内语句
当条件为真时,重复执行一个语句块。
- 语法:
K_WHILE LPAREN condition RPAREN LBRACE statement_list RBRACE ; - 示例:
VAR i : INT; i = 0; WHILE (i < 3) { OUT(i); i = i + 1; } // 将输出: // 0 // 1 // 2
条件用于 IF 和 WHILE 语句中,通常由两个表达式和一个关系操作符组成。
- 语法:
expression relational_op expression - 示例:
count < 10,x == y,a + b >= c * 2
==: 等于!=: 不等于<: 小于>: 大于<=: 小于等于>=: 大于等于
表达式由项 (terms) 和操作符组成,用于计算一个值。TL支持基本的算术表达式。
- 优先级与结合性: 乘法 (
*) 和除法 (/) 的优先级高于加法 (+) 和减法 (-)。相同优先级的操作符通常从左到右结合。括号可用于改变运算顺序。 - 语法 (ANTLR标签用于区分产生式):
expression: expression mul_div_op expression # MulDivExpr | expression add_sub_op expression # AddSubExpr | term # TermExpr ;
项是构成表达式的基本单元。
- 语法 (ANTLR标签用于区分产生式):
term: INTEGER # IntegerTerm // 整数常量 | IDENTIFIER # IdentifierTerm // 变量 | LPAREN expression RPAREN # ParenExpr // 括号括起来的表达式 ;
-
mul_div_op:*(乘),/(除) -
add_sub_op:+(加),-(减) -
表达式示例:
VAR a : INT; VAR b : INT; VAR c : INT; a = 10; b = 5; c = (a + b) * 2; // c 的值为 (10+5)*2 = 30 OUT(c); OUT(a / b); // 输出 2
// 声明变量
VAR num1 : INT;
VAR num2 : INT;
VAR sum_val : INT;
// 提示用户输入第一个数字 (通过输出特定数字间接提示)
OUT(101); // 假设 101 代表 "请输入第一个数:"
num1 = IN();
// 提示用户输入第二个数字
OUT(102); // 假设 102 代表 "请输入第二个数:"
num2 = IN();
// 计算和
sum_val = num1 + num2;
// 输出结果
OUT(103); // 假设 103 代表 "它们的和是:"
OUT(sum_val);// 比较两个数的大小
VAR x : INT;
VAR y : INT;
OUT(201); // "输入 x:"
x = IN();
OUT(202); // "输入 y:"
y = IN();
IF (x > y) {
OUT(301); // "x 大于 y"
OUT(x);
} ELSE {
IF (x < y) {
OUT(302); // "x 小于 y"
OUT(y);
} ELSE {
OUT(303); // "x 等于 y"
}
}// 输出 0 到 4
VAR i : INT;
i = 0;
WHILE (i < 5) {
OUT(i);
i = i + 1;
}
// 预期输出:
// 0
// 1
// 2
// 3
// 4这个程序会读取一个非负整数N,然后计算N的阶乘 (N!)。 为简单起见,我们假设输入的是一个较小的非负整数,以避免溢出和处理复杂的错误情况。
OUT(101);-> "请输入一个非负整数计算阶乘:"OUT(201);-> "阶乘结果是:"OUT(901);-> "错误:输入不能为负数" (如果要做负数检查)OUT(902);-> "0的阶乘是1"
// 示例1: 计算一个非负整数的阶乘 (N!)
// 假设输入的 N 是一个较小的非负整数
VAR n : INT; // 用于存储用户输入的数
VAR factorial : INT; // 用于存储计算得到的阶乘结果
VAR counter : INT; // 循环计数器
OUT(101); // 提示用户输入 N
n = IN();
IF (n < 0) {
OUT(901); // 输出错误提示:输入不能为负数 (简单处理)
} ELSE {
IF (n == 0) {
OUT(902); // 0的阶乘是1 (直接输出1)
factorial = 1; // 确保 factorial 有值
} ELSE {
factorial = 1; // 初始化阶乘结果为1
counter = 1; // 初始化计数器为1
WHILE (counter <= n) {
factorial = factorial * counter; // 累乘
counter = counter + 1; // 计数器递增
}
}
// 如果 n >= 0, 则输出结果
IF (n >= 0) { // 避免在 n < 0 时也输出 "结果是"
OUT(201); // 提示 "阶乘结果是:"
OUT(factorial);
}
}逻辑解释:
- 声明需要的变量
n,factorial,counter。 - 提示并读取用户输入的
n。 - 处理特殊情况:
- 如果
n < 0,输出错误提示(当前简单处理,实际VM可能直接输出数字901)。 - 如果
n == 0,阶乘是1。
- 如果
- 计算阶乘:
- 如果
n > 0,初始化factorial = 1和counter = 1。 - 使用
WHILE循环,当counter <= n时:factorial乘以counter。counter递增。
- 如果
- 输出最终计算得到的阶乘结果(如果输入有效)。
这个程序读取一个整数,然后判断它是正数、负数还是零。如果是正数,再判断它是奇数还是偶数。
OUT(102);-> "请输入一个整数:"OUT(202);-> "该数是正数。"OUT(203);-> "该数是负数。"OUT(204);-> "该数是零。"OUT(301);-> "并且是偶数。"OUT(302);-> "并且是奇数。"
// 示例2: 判断数字的奇偶性和正负零
VAR num : INT; // 用户输入的数字
VAR remainder : INT; // 用于存储模拟取模运算的余数
VAR tempQuotient : INT; // 用于模拟取模运算中的商
OUT(102); // "请输入一个整数:"
num = IN();
IF (num > 0) {
OUT(202); // "该数是正数。"
// 判断奇偶性 (通过 num - (num / 2) * 2 == 0 来判断)
// TL语言的 / 是整数除法
tempQuotient = num / 2;
remainder = num - (tempQuotient * 2);
IF (remainder == 0) {
OUT(301); // "并且是偶数。"
} ELSE {
OUT(302); // "并且是奇数。"
}
} ELSE {
IF (num < 0) {
OUT(203); // "该数是负数。"
// 负数的奇偶性判断可以类似,但通常关注其绝对值。为简单起见,此处不进一步判断负数的奇偶。
} ELSE {
// 如果 num 不大于0且不小于0,那么它一定是0
OUT(204); // "该数是零。"
}
}逻辑解释:
- 读取用户输入的整数
num。 - 判断正负零:
- 使用嵌套的
IF-ELSE结构。 - 首先检查
num > 0。 - 如果不是正数,再检查
num < 0。 - 如果既不大于0也不小于0,则为零。
- 使用嵌套的
- 判断奇偶性 (仅当数字为正数时):
- TL语言没有直接的取模运算符 (
%)。 - 我们可以通过公式
remainder = num - (num / divisor) * divisor来模拟取模。对于判断偶数,divisor是2。 - 由于
num / 2在TL中是整数除法,(num / 2) * 2会得到不大于num的最大偶数。 - 如果
remainder为0,则num是偶数;否则是奇数。
- TL语言没有直接的取模运算符 (
这个程序会持续读取用户输入的整数,直到用户输入0为止。然后,它会输出在此过程中用户输入的正数的个数以及所有负数的总和。
OUT(103);-> "请输入一个整数 (输入0结束):"OUT(205);-> "输入结束。"OUT(206);-> "正数个数为:"OUT(207);-> "负数总和为:"
// 示例3: 连续读取数字直到输入0,然后统计正数个数和负数总和
VAR currentNum : INT; // 当前读取的数字
VAR positiveCount : INT; // 正数的计数器
VAR negativeSum : INT; // 负数的累加和
positiveCount = 0; // 初始化计数器
negativeSum = 0; // 初始化累加和
OUT(103); // "请输入一个整数 (输入0结束):"
currentNum = IN();
WHILE (currentNum != 0) { // 当输入的数不是0时,循环继续
IF (currentNum > 0) {
positiveCount = positiveCount + 1; // 正数,则计数器加1
} ELSE {
// 如果不是正数且不是0 (因为循环条件是 !=0),那么它一定是负数
negativeSum = negativeSum + currentNum; // 负数,则累加到负数和
}
OUT(103); // 再次提示输入
currentNum = IN();
}
OUT(205); // "输入结束。"
OUT(206); // "正数个数为:"
OUT(positiveCount);
OUT(207); // "负数总和为:"
OUT(negativeSum);逻辑解释:
- 初始化
positiveCount(正数个数) 和negativeSum(负数总和) 为0。 - 首次输入: 提示用户输入第一个数字。
WHILE循环: 只要当前输入的currentNum不等于0,就执行循环体:- 条件判断:
- 如果
currentNum > 0,则positiveCount加1。 - 否则(由于循环条件排除了0,所以此时
currentNum一定是负数),将currentNum加到negativeSum。
- 如果
- 再次输入: 再次提示用户输入,并将新值赋给
currentNum,用于下一次循环的条件判断。
- 条件判断:
- 循环结束: 当用户输入
0时,循环终止。 - 输出结果: 依次输出正数的个数和负数的总和。
Collatz 序列规则:
- 从任意正整数
n开始。 - 如果
n是偶数,则下一个数是n / 2。 - 如果
n是奇数,则下一个数是3 * n + 1。 - 重复这个过程,序列最终会达到1(这是Collatz猜想,对于所有已知的正整数都成立)。
TL 语言程序示例:
OUT(101);-> "请输入一个正整数 (n > 0):"OUT(200);-> "Collatz 序列:" (只打印一次)OUT(901);-> "错误:输入必须是正整数!"
// 示例:Collatz 序列 (3n+1 问题)
VAR n : INT; // 当前序列中的数字,初始为用户输入
VAR isEvenTemp : INT; // 用于判断 n 是否为偶数的临时变量 (0代表偶数, 1代表奇数)
VAR tempCalc : INT; // 用于中间计算
OUT(101); // "请输入一个正整数 (n > 0):"
n = IN();
IF (n <= 0) { // 检查输入是否为正整数
OUT(901); // "错误:输入必须是正整数!"
} ELSE {
OUT(200); // "Collatz 序列:"
OUT(n); // 输出序列的第一个数 (即用户输入的数)
WHILE (n != 1) { // 当 n 不等于 1 时,继续循环
// 判断 n 是奇数还是偶数
// 通过 n - (n / 2) * 2 来判断余数是否为0 (模拟 n % 2)
// TL 中的 '/' 是整数除法
tempCalc = n / 2;
isEvenTemp = n - (tempCalc * 2); // 如果 n 是偶数, isEvenTemp == 0; 如果是奇数, isEvenTemp == 1 (对于正数n)
IF (isEvenTemp == 0) { // n 是偶数
n = n / 2;
} ELSE { // n 是奇数
// n = n * 3; // 不能直接这样,因为 n * 3 可能很大,先分开计算
// n = n + 1;
// 更安全的做法,使用临时变量避免过早修改 n
tempCalc = n * 3;
n = tempCalc + 1;
}
OUT(n); // 输出序列中的当前数
}
// 当 n 等于 1 时,循环结束,序列已包含最后的 1。
}程序逻辑解释:
- 声明变量:
n: 存储当前序列中的数字。isEvenTemp: 用于判断n的奇偶性。如果n - (n/2)*2的结果是0,则n是偶数。tempCalc: 用于3*n+1计算中的中间步骤,或n/2的结果。
- 输入与验证:
- 提示用户输入一个正整数并存入
n。 - 使用
IF语句检查n是否大于0。如果不是,则输出错误信息并结束(实际上是跳过后续逻辑)。
- 提示用户输入一个正整数并存入
- 序列计算与输出:
- 如果输入有效,首先输出 "Collatz 序列:" 的提示,然后输出用户输入的初始
n。 - 进入
WHILE (n != 1)循环:- 奇偶判断:
tempCalc = n / 2;(整数除法)isEvenTemp = n - (tempCalc * 2);(如果n是偶数,isEvenTemp为0;如果是奇数,为1)
- 根据奇偶性计算下一项:
IF (isEvenTemp == 0)(偶数):n = n / 2;ELSE(奇数):tempCalc = n * 3; n = tempCalc + 1;(计算3*n+1)
OUT(n);输出新计算出的n。
- 奇偶判断:
- 当
n最终等于1时,WHILE循环条件n != 1变为假,循环结束。
- 如果输入有效,首先输出 "Collatz 序列:" 的提示,然后输出用户输入的初始