stage1 部分功能测试00,01,02,06成功

This commit is contained in:
zzy
2026-07-05 18:23:33 +08:00
parent 50b07074fb
commit 51d8510b79
30 changed files with 4336 additions and 59 deletions

533
stage1/spl_lexer.c Normal file
View File

@@ -0,0 +1,533 @@
/* spl_lexer.c — SPL lexical analyzer */
#include "spl_lexer.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Character classification */
static int is_ident_start(char c) { return isalpha((unsigned char)c) || c == '_'; }
static int is_ident_cont(char c) { return isalnum((unsigned char)c) || c == '_'; }
/* Keyword lookup: if ident is a keyword, return its token type, else TOK_IDENT */
static spl_tok_type_t keyword_type(const char *ident, usize len) {
#define X(name, enum_name, dummy) \
if (len == sizeof(#name) - 1 && memcmp(ident, #name, len) == 0) \
return enum_name;
KEYWORD_TABLE
#undef X
return TOK_IDENT;
}
/* Escape sequence decoder — returns the decoded character,
* advances *s past the escape sequence, returns 0 on success,
* non-zero on error. */
int spl_decode_escape(const char **s, char *out) {
if (**s != '\\') {
*out = **s;
(*s)++;
return 0;
}
(*s)++; /* skip backslash */
switch (**s) {
case 'n':
*out = '\n';
break;
case 't':
*out = '\t';
break;
case 'r':
*out = '\r';
break;
case '\\':
*out = '\\';
break;
case '"':
*out = '"';
break;
case '\'':
*out = '\'';
break;
case '0':
*out = '\0';
break;
case 'x': {
(*s)++;
char hex[3] = {0, 0, 0};
int i;
for (i = 0; i < 2 && isxdigit((unsigned char)**s); i++, (*s)++) {
hex[i] = **s;
}
if (i == 0)
return -1;
*out = (char)strtol(hex, NULL, 16);
return 0;
}
default:
return -1;
}
(*s)++;
return 0;
}
spl_tok_vec_t spl_lex(const char *source, const char *fname) {
spl_tok_vec_t toks;
vec_init(toks);
usize line = 1;
usize col = 1;
usize offset = 0;
usize len = strlen(source);
while (offset < len) {
const char *start = source + offset;
char c = *start;
/* Skip whitespace (but not newlines — emit TOK_ENDLINE) */
if (c == ' ' || c == '\t' || c == '\r') {
offset++;
col++;
continue;
}
/* Newline */
if (c == '\n') {
spl_tok_t tok;
memset(&tok, 0, sizeof(tok));
tok.type = TOK_ENDLINE;
tok.lexeme = start;
tok.len = 1;
tok.fname = fname;
tok.offset = offset;
tok.line = line;
tok.col = col;
vec_push(toks, tok);
offset++;
line++;
col = 1;
continue;
}
/* Line comment */
if (c == '/' && offset + 1 < len && source[offset + 1] == '/') {
const char *nl = (const char *)memchr(start, '\n', len - offset);
usize clen = nl ? (usize)(nl - start) : (len - offset);
spl_tok_t tok;
memset(&tok, 0, sizeof(tok));
tok.type = TOK_LINE_COMMENT;
tok.lexeme = start;
tok.len = clen;
tok.fname = fname;
tok.offset = offset;
tok.line = line;
tok.col = col;
vec_push(toks, tok);
offset += clen;
col += clen;
continue;
}
/* Block comment */
if (c == '/' && offset + 1 < len && source[offset + 1] == '*') {
offset += 2;
col += 2;
usize depth = 1;
while (offset + 1 < len && depth > 0) {
if (source[offset] == '*' && source[offset + 1] == '/') {
depth--;
offset += 2;
col += 2;
if (depth == 0)
break;
} else if (source[offset] == '/' && source[offset + 1] == '*') {
depth++;
offset += 2;
col += 2;
} else {
if (source[offset] == '\n') {
line++;
col = 1;
} else {
col++;
}
offset++;
}
}
continue;
}
/* Char literal: 'x' */
if (c == '\'') {
offset++;
col++;
char buf[16];
int bi = 0;
memset(buf, 0, sizeof(buf));
if (offset < len) {
const char *cp = source + offset;
if (spl_decode_escape(&cp, &buf[bi])) {
buf[bi] = source[offset];
cp = source + offset + 1;
}
bi++;
offset = (usize)(cp - source);
col += (usize)(cp - (start + 1));
}
if (offset < len && source[offset] == '\'') {
offset++;
col++;
}
spl_tok_t tok;
memset(&tok, 0, sizeof(tok));
tok.type = TOK_CHAR_LITERAL;
tok.lexeme = start;
tok.len = (usize)((source + offset) - start);
tok.fname = fname;
tok.offset = start - source;
tok.line = line;
tok.col = col - tok.len;
vec_push(toks, tok);
continue;
}
/* String literal: "..." */
if (c == '"') {
offset++;
col++;
while (offset < len) {
if (source[offset] == '\\') {
offset++;
col++;
if (offset < len) {
offset++;
col++;
}
} else if (source[offset] == '"') {
offset++;
col++;
break;
} else if (source[offset] == '\n') {
line++;
col = 1;
offset++;
} else {
offset++;
col++;
}
}
spl_tok_t tok;
memset(&tok, 0, sizeof(tok));
tok.type = TOK_STRING_LITERAL;
tok.lexeme = start;
tok.len = (usize)((source + offset) - start);
tok.fname = fname;
tok.offset = start - source;
tok.line = line;
tok.col = col - tok.len;
vec_push(toks, tok);
continue;
}
/* Identifiers and keywords */
if (is_ident_start(c)) {
const char *id_start = start;
usize id_len = 0;
while (offset < len && is_ident_cont(source[offset])) {
offset++;
id_len++;
col++;
}
spl_tok_type_t tt = keyword_type(id_start, id_len);
spl_tok_t tok;
memset(&tok, 0, sizeof(tok));
tok.type = tt;
tok.lexeme = id_start;
tok.len = id_len;
tok.fname = fname;
tok.offset = id_start - source;
tok.line = line;
tok.col = col - id_len;
vec_push(toks, tok);
continue;
}
/* Numbers: integers and floats */
if (isdigit((unsigned char)c)) {
const char *num_start = start;
int is_float = 0;
/* Check for hex/bin/oct prefix */
if (c == '0' && offset + 1 < len) {
char nc = source[offset + 1];
if (nc == 'x' || nc == 'X') {
/* Hex literal */
offset += 2;
col += 2;
while (offset < len && isxdigit((unsigned char)source[offset])) {
offset++;
col++;
}
goto emit_int;
}
if (nc == 'b' || nc == 'B') {
/* Binary literal */
offset += 2;
col += 2;
while (offset < len && (source[offset] == '0' || source[offset] == '1')) {
offset++;
col++;
}
goto emit_int;
}
if (nc == 'o' || nc == 'O') {
/* Octal literal */
offset += 2;
col += 2;
while (offset < len && source[offset] >= '0' && source[offset] <= '7') {
offset++;
col++;
}
goto emit_int;
}
}
/* Decimal integer or float */
while (offset < len && isdigit((unsigned char)source[offset])) {
offset++;
col++;
}
if (offset < len && source[offset] == '.' && offset + 1 < len &&
isdigit((unsigned char)source[offset + 1])) {
is_float = 1;
offset++;
col++;
while (offset < len && isdigit((unsigned char)source[offset])) {
offset++;
col++;
}
}
if (is_float) {
spl_tok_t tok;
memset(&tok, 0, sizeof(tok));
tok.type = TOK_FLOAT_LITERAL;
tok.lexeme = num_start;
tok.len = (usize)((source + offset) - num_start);
tok.fname = fname;
tok.offset = num_start - source;
tok.line = line;
tok.col = col - tok.len;
vec_push(toks, tok);
continue;
}
emit_int: {
spl_tok_t tok;
memset(&tok, 0, sizeof(tok));
tok.type = TOK_INT_LITERAL;
tok.lexeme = num_start;
tok.len = (usize)((source + offset) - num_start);
tok.fname = fname;
tok.offset = num_start - source;
tok.line = line;
tok.col = col - tok.len;
vec_push(toks, tok);
continue;
}
}
/* Multi-character operators (longest match) */
/* Helper: try to match a two-char operator */
#define TRY_OP2(c1, c2, tok2, tok1) \
if (c == (c1) && offset + 1 < len && source[offset + 1] == (c2)) { \
spl_tok_type_t op_type = (tok2); \
usize op_len = 2; \
spl_tok_t tok; \
memset(&tok, 0, sizeof(tok)); \
tok.type = op_type; \
tok.lexeme = start; \
tok.len = op_len; \
tok.fname = fname; \
tok.offset = offset; \
tok.line = line; \
tok.col = col; \
vec_push(toks, tok); \
offset += op_len; \
col += op_len; \
continue; \
}
#define TRY_OP3(c1, c2, c3, tok3, tok2, tok1) \
if (c == (c1) && offset + 1 < len && source[offset + 1] == (c2)) { \
spl_tok_type_t op_type = (tok2); \
usize op_len = 2; \
if (offset + 2 < len && source[offset + 2] == (c3)) { \
op_type = (tok3); \
op_len = 3; \
} \
spl_tok_t tok; \
memset(&tok, 0, sizeof(tok)); \
tok.type = op_type; \
tok.lexeme = start; \
tok.len = op_len; \
tok.fname = fname; \
tok.offset = offset; \
tok.line = line; \
tok.col = col; \
vec_push(toks, tok); \
offset += op_len; \
col += op_len; \
continue; \
}
/* Three-char operators first */
TRY_OP3('<', '<', '=', TOK_ASSIGN_L_SH, TOK_L_SH, TOK_LT)
TRY_OP3('>', '>', '=', TOK_ASSIGN_R_SH, TOK_R_SH, TOK_GT)
TRY_OP3('.', '.', '.', TOK_ELLIPSIS, TOK_RANGE, TOK_DOT)
/* Two-char operators */
TRY_OP2('=', '=', TOK_EQ, TOK_ASSIGN)
TRY_OP2('!', '=', TOK_NEQ, TOK_NOT)
TRY_OP2('<', '=', TOK_LE, TOK_LT)
TRY_OP2('>', '=', TOK_GE, TOK_GT)
TRY_OP2('&', '&', TOK_AND_AND, TOK_AND)
TRY_OP2('|', '|', TOK_OR_OR, TOK_OR)
TRY_OP2('+', '=', TOK_ASSIGN_ADD, TOK_ADD)
TRY_OP2('-', '=', TOK_ASSIGN_SUB, TOK_SUB)
TRY_OP2('*', '=', TOK_ASSIGN_MUL, TOK_MUL)
TRY_OP2('/', '=', TOK_ASSIGN_DIV, TOK_DIV)
TRY_OP2('%', '=', TOK_ASSIGN_MOD, TOK_MOD)
TRY_OP2('&', '=', TOK_ASSIGN_AND, TOK_AND)
TRY_OP2('|', '=', TOK_ASSIGN_OR, TOK_OR)
TRY_OP2('^', '=', TOK_ASSIGN_XOR, TOK_XOR)
TRY_OP2('<', '-', TOK_LEFT_ARRAY, TOK_LT)
TRY_OP2('-', '>', TOK_RIGHT_ARRAY, TOK_SUB)
TRY_OP2(':', '=', TOK_COLON_ASSIGN, TOK_COLON)
/* Single-character operators */
{
spl_tok_type_t tt = TOK_UNKNOWN;
switch (c) {
case '+':
tt = TOK_ADD;
break;
case '-':
tt = TOK_SUB;
break;
case '*':
tt = TOK_MUL;
break;
case '/':
tt = TOK_DIV;
break;
case '%':
tt = TOK_MOD;
break;
case '&':
tt = TOK_AND;
break;
case '|':
tt = TOK_OR;
break;
case '^':
tt = TOK_XOR;
break;
case '~':
tt = TOK_BIT_NOT;
break;
case '!':
tt = TOK_NOT;
break;
case '<':
tt = TOK_LT;
break;
case '>':
tt = TOK_GT;
break;
case '=':
tt = TOK_ASSIGN;
break;
case '.':
tt = TOK_DOT;
break;
case ',':
tt = TOK_COMMA;
break;
case ';':
tt = TOK_SEMICOLON;
break;
case ':':
tt = TOK_COLON;
break;
case '(':
tt = TOK_L_PAREN;
break;
case ')':
tt = TOK_R_PAREN;
break;
case '[':
tt = TOK_L_BRACKET;
break;
case ']':
tt = TOK_R_BRACKET;
break;
case '{':
tt = TOK_L_BRACE;
break;
case '}':
tt = TOK_R_BRACE;
break;
case '#':
tt = TOK_SHARP;
break;
case '@':
tt = TOK_AT;
break;
case '?':
tt = TOK_COND;
break;
default:
tt = TOK_UNKNOWN;
break;
}
spl_tok_t tok;
memset(&tok, 0, sizeof(tok));
tok.type = tt;
tok.lexeme = start;
tok.len = 1;
tok.fname = fname;
tok.offset = offset;
tok.line = line;
tok.col = col;
vec_push(toks, tok);
offset++;
col++;
}
}
/* EOF token */
{
spl_tok_t tok;
memset(&tok, 0, sizeof(tok));
tok.type = TOK_EOF;
tok.lexeme = source + offset;
tok.len = 0;
tok.fname = fname;
tok.offset = offset;
tok.line = line;
tok.col = col;
vec_push(toks, tok);
}
return toks;
}