有限状态自动机模型

模型介绍

有限状态自动机(Finite Automaton, FA)是计算理论里最简洁、也最具代表性的抽象计算模型之一。它把“计算”看成一台只能处于有限个内部状态、并根据输入符号在这些状态之间转移的装置。其核心思想可以概括为:

当前状态 + 当前输入 → 下一状态

有限状态自动机常被用到词法分析当中,词法分析(Lexical Analysis)可被理解为将字符(String)序列转换为单词(Token)序列的过程,词法分析器则是以函数的形式存在。

应用举例

Java分析的词法可分为以下几例:

保留词:abstract、byte、case、catch、char、class、if、finally、new、native、void…

算数运算符:+、-、、/、%、++、–、+=、-=、/=、=

关系运算符:==、!=、>、<、>=、<=

位运算符:&、|、^、~、<<、>>、>>>

逻辑运算符:&&、||、!

赋值运算符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=

条件运算符:?

词法分析案例

下面是一段简单的 Java 代码,其功能是计算并输出一个数组中所有元素的平均值

public class AverageCalculator {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
        double sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        double average = sum / numbers.length;
        System.out.println("The average of the array is: " + average);
    }
}

词法分析器的输出结果通常以表格形式呈现。对于上述 Java 代码,词法分析器的部分输出结果如下表所示:

编号所在行单词类型单词 ID
11public保留字35
21class保留字9
31AverageCalculator标示符1
41{界符1
52public保留字35
62static保留字38
72void保留字48
82main标示符2
92(界符5
102String标示符3
112[]界符4
122args标示符4
132)界符6
142{界符1
153int保留字27
163[]界符4
173numbers标示符5
183=运算符21
193{界符1
2031数字1
213,界符7
2232数字2
233,界符7
2433数字3
253,界符7
2634数字4
273,界符7
2835数字5
293}界符2
303;界符8
314double保留字30
324sum标示符6
334=运算符21
3440数字6
354;界符8
365for保留字21
375(界符5
385int保留字27
395num标示符7
405:界符9
415numbers标示符5
425)界符6
435{界符1
446sum标示符6
456+=运算符22
466num标示符7
476;界符8
487}界符2
498double保留字30
508average标示符8
518=运算符21
528sum标示符6
538/运算符23
548numbers标示符5
558.界符10
568length标示符9
578;界符8
589System标示符10
599.界符10
609out标示符11
619.界符10
629println标示符12
639(界符5
649"The average of the array is: "字符串13
659+运算符24
669average标示符8
679)界符6
689;界符8
6910}界符2
7011}界符2

词法分析算法通常可以用有限状态自动机(Finite State Automaton,FSA)来实现。有限状态自动机是一种数学模型,它通过状态的转移来识别输入的字符序列是否属于某种模式。
在词法分析中,每个单词的识别可以看作是一个状态转移的过程。例如:

  • 初始状态:开始扫描字符。
  • 当遇到字母或下划线时,进入标识符或保留字的识别状态。
  • 当遇到数字时,进入数字的识别状态。
  • 当遇到界符或运算符时,进入相应的识别状态。

每个状态转移都对应着对输入字符的判断和处理,最终根据到达的状态确定单词的类型。
在这里插入图片描述
实际上,这种状态转移完全可以通过嵌套的 if-else 语句或者 switch-case 语句来实现。

# 简单词法分析器实现(基于循环+多层if-else)
def lexical_analyzer(code):
    # 定义保留字、界符和运算符
    reserved_words = {"if", "else", "while", "for", "int", "float", "return"}
    delimiters = {",", ";", "(", ")", "{", "}", "[", "]"}
    operators = {"+", "-", "*", "/", "=", "==", "!=", "<", ">", "<=", ">="}
    
    # 预处理:去除注释和多余空格
    lines = []
    for line in code.split('\n'):
        # 去除单行注释
        comment_index = line.find('//')
        if comment_index != -1:
            line = line[:comment_index]
        # 去除前后空格
        line = line.strip()
        if line:  # 只保留非空行
            lines.append(line)
    
    tokens = []  # 存储识别出的词法单元
    token_id = 0
    
    # 逐行处理
    for line_num, line in enumerate(lines, 1):
        pos = 0  # 当前字符位置
        length = len(line)
        
        # 循环扫描当前行的每个字符
        while pos < length:
            # 跳过空格
            if line[pos].isspace():
                pos += 1
                continue
            
            # 判断是否是字母或下划线开头(可能是保留字或标识符)
            if line[pos].isalpha() or line[pos] == '_':
                # 读取完整的单词
                start = pos
                while pos < length and (line[pos].isalnum() or line[pos] == '_'):
                    pos += 1
                word = line[start:pos]
                
                # 判断是保留字还是标识符
                if word in reserved_words:
                    token_type = "保留字"
                else:
                    token_type = "标识符"
                
                token_id += 1
                tokens.append({
                    "id": token_id,
                    "value": word,
                    "type": token_type,
                    "line": line_num
                })
            
            # 判断是否是数字开头(可能是整数或浮点数)
            elif line[pos].isdigit() or line[pos] == '.':
                start = pos
                has_dot = False
                
                # 读取完整的数字
                while pos < length:
                    if line[pos].isdigit():
                        pos += 1
                    elif line[pos] == '.':
                        # 已经有一个小数点了,不能再出现
                        if has_dot:
                            break
                        has_dot = True
                        pos += 1
                    else:
                        break
                
                number = line[start:pos]
                token_id += 1
                tokens.append({
                    "id": token_id,
                    "value": number,
                    "type": "数字",
                    "line": line_num
                })
            
            # 判断是否是运算符或界符
            else:
                # 先尝试识别双字符运算符(如==、!=等)
                if pos + 1 < length and line[pos:pos+2] in operators:
                    symbol = line[pos:pos+2]
                    pos += 2
                    token_type = "运算符"
                # 识别单字符运算符或界符
                elif line[pos] in operators:
                    symbol = line[pos]
                    pos += 1
                    token_type = "运算符"
                elif line[pos] in delimiters:
                    symbol = line[pos]
                    pos += 1
                    token_type = "界符"
                # 未知符号
                else:
                    symbol = line[pos]
                    pos += 1
                    token_type = "未知符号"
                
                token_id += 1
                tokens.append({
                    "id": token_id,
                    "value": symbol,
                    "type": token_type,
                    "line": line_num
                })
    
    return tokens

# 测试代码
if __name__ == "__main__":
    test_code = """
    // 这是一个测试代码
    int main() {
        int a = 10;
        float b = 3.14;
        if (a > b) {
            return 0;
        } else {
            return 1;
        }
    }
    """
    
    # 进行词法分析
    result = lexical_analyzer(test_code)
    
    # 输出结果
    print("词法分析结果:")
    print(f"{'ID':<5} {'值':<10} {'类型':<6} {'行号'}")
    print("-" * 35)
    for token in result:
        print(f"{token['id']:<5} {token['value']:<10} {token['type']:<6} {token['line']}")

词法分析结果如下:


词法分析结果:
ID    值          类型     行号
-----------------------------------
1     int        保留字    1
2     main       标识符    1
3     (          界符     1
4     )          界符     1
5     {          界符     1
6     int        保留字    2
7     a          标识符    2
8     =          运算符    2
9     10         数字     2
10    ;          界符     2
11    float      保留字    3
12    b          标识符    3
13    =          运算符    3
14    3.14       数字     3
15    ;          界符     3
16    if         保留字    4
17    (          界符     4
18    a          标识符    4
19    >          运算符    4
20    b          标识符    4
21    )          界符     4
22    {          界符     4
23    return     保留字    5
24    0          数字     5
25    ;          界符     5
26    }          界符     6
27    else       保留字    6
28    {          界符     6
29    return     保留字    7
30    1          数字     7
31    ;          界符     7
32    }          界符     8
33    }          界符     9
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值