已开启
提交作业-廖志宏-18-词法分析 #28
Alica_lzh创建于 2025年11月30日
提交作业-廖志宏-18-词法分析 #28
已开启
共 19 个文件变更+4324-0
| @@ -0,0 +1,461 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +// Token类型定义 | ||
| 9 | +typedef enum { | ||
| 10 | + // 关键字 | ||
| 11 | + TOKEN_AS, TOKEN_BREAK, TOKEN_CASE, TOKEN_CONST, TOKEN_CONTINUE, | ||
| 12 | + TOKEN_DO, TOKEN_ELSE, TOKEN_FOR, TOKEN_FROM, TOKEN_FUNC, | ||
| 13 | + TOKEN_IF, TOKEN_IN, TOKEN_LET, TOKEN_MAIN, TOKEN_MATCH, | ||
| 14 | + TOKEN_VAR, TOKEN_WHERE, TOKEN_WHILE, | ||
| 15 | + | ||
| 16 | + // 标识符和字面量 | ||
| 17 | + TOKEN_ID, TOKEN_NUM, | ||
| 18 | + | ||
| 19 | + // 单字符运算符 | ||
| 20 | + TOKEN_PLUS, TOKEN_MINUS, TOKEN_STAR, TOKEN_SLASH, TOKEN_ASSIGN, | ||
| 21 | + TOKEN_LPAREN, TOKEN_RPAREN, TOKEN_LBRACE, TOKEN_RBRACE, | ||
| 22 | + TOKEN_LBRACKET, TOKEN_RBRACKET, TOKEN_COMMA, TOKEN_SEMICOLON, | ||
| 23 | + TOKEN_LT, TOKEN_GT, TOKEN_NOT, TOKEN_COLON, | ||
| 24 | + | ||
| 25 | + // 双字符运算符 | ||
| 26 | + TOKEN_NE, TOKEN_GE, TOKEN_LE, TOKEN_EQ, | ||
| 27 | + TOKEN_ELLIPSIS, TOKEN_ELLIPSIS_EQ, | ||
| 28 | + | ||
| 29 | + // 注释和特殊标记 | ||
| 30 | + TOKEN_COMMENT_SINGLE, TOKEN_COMMENT_MULTI, | ||
| 31 | + TOKEN_EOF, TOKEN_ERROR | ||
| 32 | +} TokenType; | ||
| 33 | + | ||
| 34 | +// Token结构体 | ||
| 35 | +typedef struct { | ||
| 36 | + TokenType type; | ||
| 37 | + char lexeme[256]; | ||
| 38 | + int line; | ||
| 39 | + int column; | ||
| 40 | +} Token; | ||
| 41 | + | ||
| 42 | +// 关键字查找表 | ||
| 43 | +typedef struct { | ||
| 44 | + char* keyword; | ||
| 45 | + TokenType type; | ||
| 46 | +} Keyword; | ||
| 47 | + | ||
| 48 | +Keyword keywords[] = { | ||
| 49 | + {"as", TOKEN_AS}, | ||
| 50 | + {"break", TOKEN_BREAK}, | ||
| 51 | + {"case", TOKEN_CASE}, | ||
| 52 | + {"const", TOKEN_CONST}, | ||
| 53 | + {"continue", TOKEN_CONTINUE}, | ||
| 54 | + {"do", TOKEN_DO}, | ||
| 55 | + {"else", TOKEN_ELSE}, | ||
| 56 | + {"for", TOKEN_FOR}, | ||
| 57 | + {"from", TOKEN_FROM}, | ||
| 58 | + {"func", TOKEN_FUNC}, | ||
| 59 | + {"if", TOKEN_IF}, | ||
| 60 | + {"in", TOKEN_IN}, | ||
| 61 | + {"let", TOKEN_LET}, | ||
| 62 | + {"main", TOKEN_MAIN}, | ||
| 63 | + {"match", TOKEN_MATCH}, | ||
| 64 | + {"var", TOKEN_VAR}, | ||
| 65 | + {"where", TOKEN_WHERE}, | ||
| 66 | + {"while", TOKEN_WHILE}, | ||
| 67 | + {NULL, TOKEN_ERROR} | ||
| 68 | +}; | ||
| 69 | + | ||
| 70 | +// 词法分析器状态 | ||
| 71 | +typedef struct { | ||
| 72 | + const char* source; | ||
| 73 | + int current_pos; | ||
| 74 | + int line; | ||
| 75 | + int column; | ||
| 76 | + bool has_error; | ||
| 77 | +} Lexer; | ||
| 78 | + | ||
| 79 | +// 初始化词法分析器 | ||
| 80 | +void init_lexer(Lexer* lexer, const char* source) { | ||
| 81 | + lexer->source = source; | ||
| 82 | + lexer->current_pos = 0; | ||
| 83 | + lexer->line = 1; | ||
| 84 | + lexer->column = 1; | ||
| 85 | + lexer->has_error = false; | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +// 获取当前字符 | ||
| 89 | +char current_char(Lexer* lexer) { | ||
| 90 | + return lexer->source[lexer->current_pos]; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +// 查看下一个字符(不移动位置) | ||
| 94 | +char peek_char(Lexer* lexer, int offset) { | ||
| 95 | + return lexer->source[lexer->current_pos + offset]; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +// 前进到下一个字符 | ||
| 99 | +void advance(Lexer* lexer) { | ||
| 100 | + if (current_char(lexer) == '\n') { | ||
| 101 | + lexer->line++; | ||
| 102 | + lexer->column = 1; | ||
| 103 | + } | ||
| 104 | + else { | ||
| 105 | + lexer->column++; | ||
| 106 | + } | ||
| 107 | + lexer->current_pos++; | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +// 跳过空白字符 | ||
| 111 | +void skip_whitespace(Lexer* lexer) { | ||
| 112 | + while (isspace(current_char(lexer))) { | ||
| 113 | + advance(lexer); | ||
| 114 | + } | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +// 检查关键字 | ||
| 118 | +TokenType check_keyword(const char* lexeme) { | ||
| 119 | + for (int i = 0; keywords[i].keyword != NULL; i++) { | ||
| 120 | + if (strcmp(lexeme, keywords[i].keyword) == 0) { | ||
| 121 | + return keywords[i].type; | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + return TOKEN_ID; | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +// 识别标识符或关键字 | ||
| 128 | +Token identifier_or_keyword(Lexer* lexer) { | ||
| 129 | + Token token; | ||
| 130 | + token.line = lexer->line; | ||
| 131 | + token.column = lexer->column; | ||
| 132 | + | ||
| 133 | + int i = 0; | ||
| 134 | + while (isalnum(current_char(lexer))) { | ||
| 135 | + if (i < 255) { | ||
| 136 | + token.lexeme[i++] = current_char(lexer); | ||
| 137 | + } | ||
| 138 | + advance(lexer); | ||
| 139 | + } | ||
| 140 | + token.lexeme[i] = '\0'; | ||
| 141 | + | ||
| 142 | + token.type = check_keyword(token.lexeme); | ||
| 143 | + return token; | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +// 识别数字字面量 - 修复版本 | ||
| 147 | +Token number(Lexer* lexer) { | ||
| 148 | + Token token; | ||
| 149 | + token.type = TOKEN_NUM; | ||
| 150 | + token.line = lexer->line; | ||
| 151 | + token.column = lexer->column; | ||
| 152 | + | ||
| 153 | + int i = 0; | ||
| 154 | + while (isdigit(current_char(lexer))) { | ||
| 155 | + if (i < 255) { | ||
| 156 | + token.lexeme[i++] = current_char(lexer); | ||
| 157 | + } | ||
| 158 | + advance(lexer); | ||
| 159 | + } | ||
| 160 | + token.lexeme[i] = '\0'; | ||
| 161 | + | ||
| 162 | + return token; | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +// 处理单行注释 | ||
| 166 | +void skip_single_line_comment(Lexer* lexer) { | ||
| 167 | + while (current_char(lexer) != '\n' && current_char(lexer) != '\0') { | ||
| 168 | + advance(lexer); | ||
| 169 | + } | ||
| 170 | +} | ||
| 171 | + | ||
| 172 | +// 处理多行注释 | ||
| 173 | +bool skip_multi_line_comment(Lexer* lexer) { | ||
| 174 | + advance(lexer); // 跳过 '*' | ||
| 175 | + | ||
| 176 | + while (true) { | ||
| 177 | + if (current_char(lexer) == '\0') { | ||
| 178 | + return false; // 未闭合的注释 | ||
| 179 | + } | ||
| 180 | + if (current_char(lexer) == '*' && peek_char(lexer, 1) == '/') { | ||
| 181 | + advance(lexer); // 跳过 '*' | ||
| 182 | + advance(lexer); // 跳过 '/' | ||
| 183 | + return true; | ||
| 184 | + } | ||
| 185 | + advance(lexer); | ||
| 186 | + } | ||
| 187 | +} | ||
| 188 | + | ||
| 189 | +// 获取下一个token - 修复版本 | ||
| 190 | +Token next_token(Lexer* lexer) { | ||
| 191 | + skip_whitespace(lexer); | ||
| 192 | + | ||
| 193 | + Token token; | ||
| 194 | + token.line = lexer->line; | ||
| 195 | + token.column = lexer->column; | ||
| 196 | + token.lexeme[0] = current_char(lexer); | ||
| 197 | + token.lexeme[1] = '\0'; | ||
| 198 | + | ||
| 199 | + if (current_char(lexer) == '\0') { | ||
| 200 | + token.type = TOKEN_EOF; | ||
| 201 | + return token; | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + char c = current_char(lexer); | ||
| 205 | + | ||
| 206 | + // 识别标识符(必须以字母开头) | ||
| 207 | + if (isalpha(c)) { | ||
| 208 | + return identifier_or_keyword(lexer); | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + // 识别数字 | ||
| 212 | + if (isdigit(c)) { | ||
| 213 | + return number(lexer); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + // 处理注释和运算符 | ||
| 217 | + switch (c) { | ||
| 218 | + case '+': | ||
| 219 | + token.type = TOKEN_PLUS; | ||
| 220 | + advance(lexer); | ||
| 221 | + break; | ||
| 222 | + | ||
| 223 | + case '-': | ||
| 224 | + token.type = TOKEN_MINUS; | ||
| 225 | + advance(lexer); | ||
| 226 | + break; | ||
| 227 | + | ||
| 228 | + case '*': | ||
| 229 | + token.type = TOKEN_STAR; | ||
| 230 | + advance(lexer); | ||
| 231 | + break; | ||
| 232 | + | ||
| 233 | + case '/': | ||
| 234 | + if (peek_char(lexer, 1) == '/') { | ||
| 235 | + // 单行注释 | ||
| 236 | + skip_single_line_comment(lexer); | ||
| 237 | + return next_token(lexer); | ||
| 238 | + } | ||
| 239 | + else if (peek_char(lexer, 1) == '*') { | ||
| 240 | + // 多行注释 | ||
| 241 | + if (!skip_multi_line_comment(lexer)) { | ||
| 242 | + token.type = TOKEN_ERROR; | ||
| 243 | + strcpy(token.lexeme, "Unclosed multi-line comment"); | ||
| 244 | + lexer->has_error = true; | ||
| 245 | + } | ||
| 246 | + return next_token(lexer); | ||
| 247 | + } | ||
| 248 | + else { | ||
| 249 | + token.type = TOKEN_SLASH; | ||
| 250 | + advance(lexer); | ||
| 251 | + } | ||
| 252 | + break; | ||
| 253 | + | ||
| 254 | + case '=': | ||
| 255 | + if (peek_char(lexer, 1) == '=') { | ||
| 256 | + token.type = TOKEN_EQ; | ||
| 257 | + strcpy(token.lexeme, "=="); | ||
| 258 | + advance(lexer); | ||
| 259 | + advance(lexer); | ||
| 260 | + } | ||
| 261 | + else { | ||
| 262 | + token.type = TOKEN_ASSIGN; | ||
| 263 | + advance(lexer); | ||
| 264 | + } | ||
| 265 | + break; | ||
| 266 | + | ||
| 267 | + case '!': | ||
| 268 | + if (peek_char(lexer, 1) == '=') { | ||
| 269 | + token.type = TOKEN_NE; | ||
| 270 | + strcpy(token.lexeme, "!="); | ||
| 271 | + advance(lexer); | ||
| 272 | + advance(lexer); | ||
| 273 | + } | ||
| 274 | + else { | ||
| 275 | + token.type = TOKEN_NOT; | ||
| 276 | + advance(lexer); | ||
| 277 | + } | ||
| 278 | + break; | ||
| 279 | + | ||
| 280 | + case '>': | ||
| 281 | + if (peek_char(lexer, 1) == '=') { | ||
| 282 | + token.type = TOKEN_GE; | ||
| 283 | + strcpy(token.lexeme, ">="); | ||
| 284 | + advance(lexer); | ||
| 285 | + advance(lexer); | ||
| 286 | + } | ||
| 287 | + else { | ||
| 288 | + token.type = TOKEN_GT; | ||
| 289 | + advance(lexer); | ||
| 290 | + } | ||
| 291 | + break; | ||
| 292 | + | ||
| 293 | + case '<': | ||
| 294 | + if (peek_char(lexer, 1) == '=') { | ||
| 295 | + token.type = TOKEN_LE; | ||
| 296 | + strcpy(token.lexeme, "<="); | ||
| 297 | + advance(lexer); | ||
| 298 | + advance(lexer); | ||
| 299 | + } | ||
| 300 | + else { | ||
| 301 | + token.type = TOKEN_LT; | ||
| 302 | + advance(lexer); | ||
| 303 | + } | ||
| 304 | + break; | ||
| 305 | + | ||
| 306 | + case '(': | ||
| 307 | + token.type = TOKEN_LPAREN; | ||
| 308 | + advance(lexer); | ||
| 309 | + break; | ||
| 310 | + | ||
| 311 | + case ')': | ||
| 312 | + token.type = TOKEN_RPAREN; | ||
| 313 | + advance(lexer); | ||
| 314 | + break; | ||
| 315 | + | ||
| 316 | + case '{': | ||
| 317 | + token.type = TOKEN_LBRACE; | ||
| 318 | + advance(lexer); | ||
| 319 | + break; | ||
| 320 | + | ||
| 321 | + case '}': | ||
| 322 | + token.type = TOKEN_RBRACE; | ||
| 323 | + advance(lexer); | ||
| 324 | + break; | ||
| 325 | + | ||
| 326 | + case '[': | ||
| 327 | + token.type = TOKEN_LBRACKET; | ||
| 328 | + advance(lexer); | ||
| 329 | + break; | ||
| 330 | + | ||
| 331 | + case ']': | ||
| 332 | + token.type = TOKEN_RBRACKET; | ||
| 333 | + advance(lexer); | ||
| 334 | + break; | ||
| 335 | + | ||
| 336 | + case ',': | ||
| 337 | + token.type = TOKEN_COMMA; | ||
| 338 | + advance(lexer); | ||
| 339 | + break; | ||
| 340 | + | ||
| 341 | + case ';': | ||
| 342 | + token.type = TOKEN_SEMICOLON; | ||
| 343 | + advance(lexer); | ||
| 344 | + break; | ||
| 345 | + | ||
| 346 | + case ':': | ||
| 347 | + token.type = TOKEN_COLON; | ||
| 348 | + advance(lexer); | ||
| 349 | + break; | ||
| 350 | + | ||
| 351 | + case '.': | ||
| 352 | + if (peek_char(lexer, 1) == '.' && peek_char(lexer, 2) == '.') { | ||
| 353 | + if (peek_char(lexer, 3) == '=') { | ||
| 354 | + token.type = TOKEN_ELLIPSIS_EQ; | ||
| 355 | + strcpy(token.lexeme, "...="); | ||
| 356 | + advance(lexer); | ||
| 357 | + advance(lexer); | ||
| 358 | + advance(lexer); | ||
| 359 | + advance(lexer); | ||
| 360 | + } | ||
| 361 | + else { | ||
| 362 | + token.type = TOKEN_ELLIPSIS; | ||
| 363 | + strcpy(token.lexeme, "..."); | ||
| 364 | + advance(lexer); | ||
| 365 | + advance(lexer); | ||
| 366 | + advance(lexer); | ||
| 367 | + } | ||
| 368 | + } | ||
| 369 | + else { | ||
| 370 | + token.type = TOKEN_ERROR; | ||
| 371 | + strcpy(token.lexeme, "Invalid token: single dot not supported"); | ||
| 372 | + lexer->has_error = true; | ||
| 373 | + advance(lexer); | ||
| 374 | + } | ||
| 375 | + break; | ||
| 376 | + | ||
| 377 | + default: | ||
| 378 | + token.type = TOKEN_ERROR; | ||
| 379 | + sprintf(token.lexeme, "Unknown character: '%c'", c); | ||
| 380 | + lexer->has_error = true; | ||
| 381 | + advance(lexer); | ||
| 382 | + break; | ||
| 383 | + } | ||
| 384 | + | ||
| 385 | + return token; | ||
| 386 | +} | ||
| 387 | + | ||
| 388 | +// 打印token信息 | ||
| 389 | +void print_token(Token token) { | ||
| 390 | + const char* type_names[] = { | ||
| 391 | + "AS", "BREAK", "CASE", "CONST", "CONTINUE", "DO", "ELSE", "FOR", "FROM", "FUNC", | ||
| 392 | + "IF", "IN", "LET", "MAIN", "MATCH", "VAR", "WHERE", "WHILE", | ||
| 393 | + "ID", "NUM", | ||
| 394 | + "PLUS", "MINUS", "STAR", "SLASH", "ASSIGN", "LPAREN", "RPAREN", "LBRACE", "RBRACE", | ||
| 395 | + "LBRACKET", "RBRACKET", "COMMA", "SEMICOLON", "LT", "GT", "NOT", "COLON", | ||
| 396 | + "NE", "GE", "LE", "EQ", "ELLIPSIS", "ELLIPSIS_EQ", | ||
| 397 | + "COMMENT_SINGLE", "COMMENT_MULTI", "EOF", "ERROR" | ||
| 398 | + }; | ||
| 399 | + | ||
| 400 | + if (token.type == TOKEN_ERROR) { | ||
| 401 | + printf("Line %d, Column %d: ERROR - %s\n", | ||
| 402 | + token.line, token.column, token.lexeme); | ||
| 403 | + } | ||
| 404 | + else { | ||
| 405 | + printf("Line %d, Column %d: %-20s %s\n", | ||
| 406 | + token.line, token.column, type_names[token.type], token.lexeme); | ||
| 407 | + } | ||
| 408 | +} | ||
| 409 | + | ||
| 410 | +// 主函数 - 测试词法分析器 | ||
| 411 | +int main() { | ||
| 412 | + const char* corrected_test_code = | ||
| 413 | + "func main() {\n" | ||
| 414 | + " let x = 123;\n" | ||
| 415 | + " let userName = testValue; // 修正:使用合法标识符\n" | ||
| 416 | + " let abc123 = 456; // 修正:标识符以字母开头\n" | ||
| 417 | + " let count = 100; // 修正:使用整数而不是浮点数\n" | ||
| 418 | + " if x > 100 {\n" | ||
| 419 | + " break;\n" | ||
| 420 | + " }\n" | ||
| 421 | + " for i in 1...10 { // 修正:使用正确的区间运算符\n" | ||
| 422 | + " const totalScore = 789; // 修正:使用合法标识符\n" | ||
| 423 | + " }\n" | ||
| 424 | + " let result = value; // 修正:使用合法标识符\n" | ||
| 425 | + " let range = 1...10; // 修正:使用正确的区间运算符\n" | ||
| 426 | + " /* 这是一个正常的多行注释 */\n" | ||
| 427 | + " let validName = value; // 正常代码\n" | ||
| 428 | + " // 单行注释:计算总和\n" | ||
| 429 | + " let sum = a + b * c;\n" | ||
| 430 | + "}\n"; | ||
| 431 | + | ||
| 432 | + printf("Analyzing corrected code (no lexical errors):\n%s\n", corrected_test_code); | ||
| 433 | + printf("Tokens:\n"); | ||
| 434 | + | ||
| 435 | + Lexer lexer; | ||
| 436 | + init_lexer(&lexer, corrected_test_code); | ||
| 437 | + | ||
| 438 | + Token token; | ||
| 439 | + int token_count = 0; | ||
| 440 | + int max_tokens = 200; | ||
| 441 | + int error_count = 0; | ||
| 442 | + | ||
| 443 | + do { | ||
| 444 | + token = next_token(&lexer); | ||
| 445 | + print_token(token); | ||
| 446 | + if (token.type == TOKEN_ERROR) { | ||
| 447 | + error_count++; | ||
| 448 | + } | ||
| 449 | + token_count++; | ||
| 450 | + } while (token.type != TOKEN_EOF && token_count < max_tokens); | ||
| 451 | + | ||
| 452 | + printf("\nTotal errors detected: %d\n", error_count); | ||
| 453 | + if (error_count > 0) { | ||
| 454 | + printf("Lexical analysis completed with errors.\n"); | ||
| 455 | + } | ||
| 456 | + else { | ||
| 457 | + printf("Lexical analysis completed successfully.\n"); | ||
| 458 | + } | ||
| 459 | + | ||
| 460 | + return 0; | ||
| 461 | +} | ||
Binary files do not support preview