Skip to content

imxcstar/TinyLanguage

Repository files navigation

TL (Tiny Language) 语法说明文档

1. 简介

TL (Tiny Language) 是一种由AI设计和制作为特定虚拟机设计的极简命令式编程语言。它支持基本的变量声明、赋值、算术运算、条件控制和循环结构。本文档旨在详细说明TL语言的语法规则。

2. 基本约定

2.1. 注释 (Comments)

TL语言支持单行注释。注释以 // 开始,直至该行结束。

// 这是一个单行注释
VAR x : INT; // 变量声明也可以有行尾注释

2.2. 标识符 (Identifiers)

标识符用于命名变量。

  • 规则: 以字母 (a-z, A-Z) 或下划线 (_) 开头,后跟任意数量的字母、数字 (0-9) 或下划线。
  • 示例: myVariable, counter, _tempVal, value1

2.3. 整数 (Integers)

TL语言目前仅支持整数类型。

  • 规则: 整数可以包含一个可选的负号 (-),后跟一位或多位数字。
  • 示例: 123, 0, -42, 10000

2.4. 关键字 (Keywords)

TL语言有以下保留关键字,不能用作标识符:

  • VAR: 用于声明变量。
  • INT: 指定变量类型为整数。
  • IF: 条件语句的开始。
  • ELSE: IF 语句的可选分支。
  • WHILE: 循环语句的开始。
  • IN: 用于从标准输入读取整数。
  • OUT: 用于向标准输出打印表达式的值。

2.5. 空白与格式

空白符(空格、制表符、换行符)用于分隔语法单元,但通常不影响程序逻辑(除非在字符串字面量中,但TL目前不支持字符串字面量)。良好的缩进和空白使用可以提高代码可读性。

3. 程序结构

3.1. program (程序)

一个完整的TL程序由一个语句列表 (statement_list) 和一个文件结束符 (EOF) 构成。

  • 语法: statement_list EOF

3.2. statement_list (语句列表)

语句列表包含零个或多个语句。它可以是程序的主体,也可以是 IFWHILE 语句块内部的内容。

  • 语法: statement* (表示零个或多个 statement)
  • 示例:
    // 一个包含多个语句的语句列表
    VAR x : INT;
    x = 10;
    OUT(x);
    // 一个空的语句列表 (例如在一个IF块中)
    IF (x > 10) {
        // 空块
    }

4. 语句 (Statements)

4.1. statement (语句)

一个语句是语言中一个完整的执行单元。TL支持以下类型的语句,除 IFWHILE 语句外,其他语句通常以分号 (;) 结尾。

  • 语法:
    statement:
        declaration ';'
        | assignment ';'
        | input_statement ';'
        | output_statement ';'
        | if_statement
        | while_statement
        ;

4.2. declaration (变量声明)

声明一个或多个指定类型的变量。在当前版本的TL中,所有变量都是全局整数类型。

  • 语法: K_VAR IDENTIFIER ':' K_INT_TYPE ;
  • 示例:
    VAR count : INT;
    VAR tempValue : INT;

4.3. assignment (赋值语句)

将一个表达式的值赋给一个已声明的变量。

  • 语法: IDENTIFIER ASSIGN expression ;
  • 示例:
    count = 0;
    sum = value1 + value2;
    count = count + 1;

4.4. input_statement (输入语句)

从标准输入读取一个整数,并将其赋给一个变量。

  • 语法: IDENTIFIER ASSIGN K_IN LPAREN RPAREN ;
  • 示例:
    VAR userInput : INT;
    userInput = IN();

4.5. output_statement (输出语句)

计算一个表达式的值,并将其输出到标准输出。

  • 语法: K_OUT LPAREN expression RPAREN ;
  • 示例:
    VAR result : INT;
    result = 10 * 5;
    OUT(result);       // 输出 50
    OUT(result + 5); // 输出 55
    OUT(100);          // 直接输出常量

4.6. if_statement (IF 条件语句)

根据条件的真假执行不同的语句块。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,则不执行块内语句

4.7. while_statement (WHILE 循环语句)

当条件为真时,重复执行一个语句块。

  • 语法: 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

5. 条件 (Conditions)

5.1. condition (条件)

条件用于 IFWHILE 语句中,通常由两个表达式和一个关系操作符组成。

  • 语法: expression relational_op expression
  • 示例: count < 10, x == y, a + b >= c * 2

5.2. relational_op (关系操作符)

  • == : 等于
  • != : 不等于
  • < : 小于
  • > : 大于
  • <= : 小于等于
  • >= : 大于等于

6. 表达式 (Expressions)

6.1. expression (表达式)

表达式由项 (terms) 和操作符组成,用于计算一个值。TL支持基本的算术表达式。

  • 优先级与结合性: 乘法 (*) 和除法 (/) 的优先级高于加法 (+) 和减法 (-)。相同优先级的操作符通常从左到右结合。括号可用于改变运算顺序。
  • 语法 (ANTLR标签用于区分产生式):
    expression:
        expression mul_div_op expression # MulDivExpr
        | expression add_sub_op expression # AddSubExpr
        | term                         # TermExpr
        ;

6.2. term (项)

项是构成表达式的基本单元。

  • 语法 (ANTLR标签用于区分产生式):
    term:
        INTEGER                        # IntegerTerm    // 整数常量
        | IDENTIFIER                   # IdentifierTerm // 变量
        | LPAREN expression RPAREN     # ParenExpr      // 括号括起来的表达式
        ;

6.3. 算术操作符

  • 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

7. 完整示例程序

示例 1: 基本输入输出和赋值

// 声明变量
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);

示例 2: 使用IF-ELSE语句

// 比较两个数的大小
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"
    }
}

示例 3: 使用WHILE循环

// 输出 0 到 4
VAR i : INT;
i = 0;
WHILE (i < 5) {
    OUT(i);
    i = i + 1;
}
// 预期输出:
// 0
// 1
// 2
// 3
// 4

示例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);
    }
}

逻辑解释:

  1. 声明需要的变量 n, factorial, counter
  2. 提示并读取用户输入的 n
  3. 处理特殊情况:
    • 如果 n < 0,输出错误提示(当前简单处理,实际VM可能直接输出数字901)。
    • 如果 n == 0,阶乘是1。
  4. 计算阶乘:
    • 如果 n > 0,初始化 factorial = 1counter = 1
    • 使用 WHILE 循环,当 counter <= n 时:
      • factorial 乘以 counter
      • counter 递增。
  5. 输出最终计算得到的阶乘结果(如果输入有效)。

示例5: 判断数字的奇偶性和正负零

这个程序读取一个整数,然后判断它是正数、负数还是零。如果是正数,再判断它是奇数还是偶数。

  • 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); // "该数是零。"
    }
}

逻辑解释:

  1. 读取用户输入的整数 num
  2. 判断正负零:
    • 使用嵌套的 IF-ELSE 结构。
    • 首先检查 num > 0
    • 如果不是正数,再检查 num < 0
    • 如果既不大于0也不小于0,则为零。
  3. 判断奇偶性 (仅当数字为正数时):
    • TL语言没有直接的取模运算符 (%)。
    • 我们可以通过公式 remainder = num - (num / divisor) * divisor 来模拟取模。对于判断偶数,divisor2
    • 由于 num / 2 在TL中是整数除法, (num / 2) * 2 会得到不大于 num 的最大偶数。
    • 如果 remainder0,则 num 是偶数;否则是奇数。

示例6: 连续输入数字直到0,统计正数个数和负数之和

这个程序会持续读取用户输入的整数,直到用户输入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);

逻辑解释:

  1. 初始化 positiveCount (正数个数) 和 negativeSum (负数总和) 为0。
  2. 首次输入: 提示用户输入第一个数字。
  3. WHILE 循环: 只要当前输入的 currentNum 不等于 0,就执行循环体:
    • 条件判断:
      • 如果 currentNum > 0,则 positiveCount 加1。
      • 否则(由于循环条件排除了0,所以此时 currentNum 一定是负数),将 currentNum 加到 negativeSum
    • 再次输入: 再次提示用户输入,并将新值赋给 currentNum,用于下一次循环的条件判断。
  4. 循环结束: 当用户输入 0 时,循环终止。
  5. 输出结果: 依次输出正数的个数和负数的总和。

示例7: Collatz序列 (3n+1 问题) 计算,并输出序列中的每一个数,直到达到1为止。

Collatz 序列规则:

  1. 从任意正整数 n 开始。
  2. 如果 n 是偶数,则下一个数是 n / 2
  3. 如果 n 是奇数,则下一个数是 3 * n + 1
  4. 重复这个过程,序列最终会达到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。
}

程序逻辑解释:

  1. 声明变量:
    • n: 存储当前序列中的数字。
    • isEvenTemp: 用于判断 n 的奇偶性。如果 n - (n/2)*2 的结果是0,则 n 是偶数。
    • tempCalc: 用于 3*n+1 计算中的中间步骤,或 n/2 的结果。
  2. 输入与验证:
    • 提示用户输入一个正整数并存入 n
    • 使用 IF 语句检查 n 是否大于0。如果不是,则输出错误信息并结束(实际上是跳过后续逻辑)。
  3. 序列计算与输出:
    • 如果输入有效,首先输出 "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 变为假,循环结束。

About

简单语言编译器和VM

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published