add c and jai

This commit is contained in:
2026-07-13 13:02:47 -04:00
parent cf8342f8d4
commit c5b14d7c41
33 changed files with 6306 additions and 0 deletions

422
c/lexer/lexer_lua.c Normal file
View File

@@ -0,0 +1,422 @@
// lexer_lua.c -- Lua language tokenizer
//
// Same pattern as lexer_go.c: a get_next_token() loop that advances a cursor
// through the source, producing Token structs. After each token we paint
// the per-byte token-type array. A second pass marks identifiers before '('
// as functions.
#include "lexer/lexer.h"
#include <ctype.h>
#include <string.h>
////////////////////////////////
// Lua keyword tables
typedef struct LuaKeywordEntry {
const char *word;
Token_Type type;
} LuaKeywordEntry;
static const LuaKeywordEntry lua_keywords[] = {
// Keywords
{"and", TOK_KEYWORD},
{"break", TOK_KEYWORD},
{"do", TOK_KEYWORD},
{"else", TOK_KEYWORD},
{"elseif", TOK_KEYWORD},
{"end", TOK_KEYWORD},
{"for", TOK_KEYWORD},
{"function", TOK_KEYWORD},
{"goto", TOK_KEYWORD},
{"if", TOK_KEYWORD},
{"in", TOK_KEYWORD},
{"local", TOK_KEYWORD},
{"not", TOK_KEYWORD},
{"or", TOK_KEYWORD},
{"repeat", TOK_KEYWORD},
{"return", TOK_KEYWORD},
{"then", TOK_KEYWORD},
{"until", TOK_KEYWORD},
{"while", TOK_KEYWORD},
// Values
{"true", TOK_VALUE},
{"false", TOK_VALUE},
{"nil", TOK_VALUE},
// Built-in globals / types (use TOK_TYPE for distinct coloring)
{"self", TOK_TYPE},
// Built-in functions (TOK_MODIFIER for distinct coloring)
{"assert", TOK_MODIFIER},
{"collectgarbage", TOK_MODIFIER},
{"dofile", TOK_MODIFIER},
{"error", TOK_MODIFIER},
{"getmetatable", TOK_MODIFIER},
{"ipairs", TOK_MODIFIER},
{"load", TOK_MODIFIER},
{"loadfile", TOK_MODIFIER},
{"next", TOK_MODIFIER},
{"pairs", TOK_MODIFIER},
{"pcall", TOK_MODIFIER},
{"print", TOK_MODIFIER},
{"rawequal", TOK_MODIFIER},
{"rawget", TOK_MODIFIER},
{"rawlen", TOK_MODIFIER},
{"rawset", TOK_MODIFIER},
{"require", TOK_MODIFIER},
{"select", TOK_MODIFIER},
{"setmetatable", TOK_MODIFIER},
{"tonumber", TOK_MODIFIER},
{"tostring", TOK_MODIFIER},
{"type", TOK_MODIFIER},
{"unpack", TOK_MODIFIER},
{"xpcall", TOK_MODIFIER},
};
#define LUA_KEYWORD_COUNT (S32)(sizeof(lua_keywords) / sizeof(lua_keywords[0]))
static Token_Type lua_lookup_keyword(const char *word, S32 len) {
for (S32 i = 0; i < LUA_KEYWORD_COUNT; i++) {
const char *kw = lua_keywords[i].word;
S32 kwlen = (S32)strlen(kw);
if (kwlen == len && memcmp(kw, word, len) == 0)
return lua_keywords[i].type;
}
return TOK_IDENTIFIER;
}
////////////////////////////////
// Character helpers
static B32 lua_is_ident_start(char c) {
return isalpha((unsigned char)c) || c == '_';
}
static B32 lua_is_ident_char(char c) {
return isalnum((unsigned char)c) || c == '_';
}
static B32 lua_is_hex(char c) {
return isdigit((unsigned char)c) ||
(c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
////////////////////////////////
// Long bracket level detection
// Returns the level (number of '=' signs) if we're at a long bracket opening
// like [[ or [==[ etc. Returns -1 if not a long bracket.
static S32 lua_long_bracket_level(const char *p, const char *max_p) {
if (p >= max_p || *p != '[') return -1;
p++;
S32 level = 0;
while (p < max_p && *p == '=') { level++; p++; }
if (p < max_p && *p == '[') return level;
return -1;
}
// Scan past the closing long bracket of the given level.
// Cursor should be right after the opening [=*[.
static void lua_scan_long_bracket_close(Tokenizer *tok, S32 level) {
while (tok->t < tok->max_t) {
if (*tok->t == ']') {
const char *p = tok->t + 1;
S32 count = 0;
while (p < tok->max_t && *p == '=' && count < level) { count++; p++; }
if (count == level && p < tok->max_t && *p == ']') {
tok->t = p + 1;
return;
}
}
tok->t++;
}
}
////////////////////////////////
// Individual token parsers
static Token lua_parse_identifier(Tokenizer *tok) {
Token token;
token.start = (S32)(tok->start_t - tok->buf);
const char *begin = tok->t;
while (tok->t < tok->max_t && lua_is_ident_char(*tok->t))
tok->t++;
S32 len = (S32)(tok->t - begin);
token.type = lua_lookup_keyword(begin, len);
token.len = (S32)(tok->t - tok->start_t);
return token;
}
static Token lua_parse_number(Tokenizer *tok) {
Token token;
token.start = (S32)(tok->start_t - tok->buf);
token.type = TOK_NUMBER;
char c = *tok->t;
tok->t++;
if (c == '0' && tok->t < tok->max_t && (*tok->t == 'x' || *tok->t == 'X')) {
// Hex literal
tok->t++;
while (tok->t < tok->max_t && lua_is_hex(*tok->t)) tok->t++;
// Hex float
if (tok->t < tok->max_t && *tok->t == '.') {
tok->t++;
while (tok->t < tok->max_t && lua_is_hex(*tok->t)) tok->t++;
}
// Hex exponent (p/P)
if (tok->t < tok->max_t && (*tok->t == 'p' || *tok->t == 'P')) {
tok->t++;
if (tok->t < tok->max_t && (*tok->t == '+' || *tok->t == '-')) tok->t++;
while (tok->t < tok->max_t && isdigit((unsigned char)*tok->t)) tok->t++;
}
} else {
// Decimal
while (tok->t < tok->max_t && isdigit((unsigned char)*tok->t)) tok->t++;
// Float
if (tok->t < tok->max_t && *tok->t == '.') {
if ((tok->t + 1) < tok->max_t && isdigit((unsigned char)*(tok->t + 1))) {
tok->t++;
while (tok->t < tok->max_t && isdigit((unsigned char)*tok->t)) tok->t++;
} else if ((tok->t + 1) >= tok->max_t || !lua_is_ident_start(*(tok->t + 1))) {
tok->t++;
}
}
// Exponent
if (tok->t < tok->max_t && (*tok->t == 'e' || *tok->t == 'E')) {
tok->t++;
if (tok->t < tok->max_t && (*tok->t == '+' || *tok->t == '-')) tok->t++;
while (tok->t < tok->max_t && isdigit((unsigned char)*tok->t)) tok->t++;
}
}
token.len = (S32)(tok->t - tok->start_t);
return token;
}
static Token lua_parse_dot_number(Tokenizer *tok) {
Token token;
token.start = (S32)(tok->start_t - tok->buf);
token.type = TOK_NUMBER;
tok->t++; // skip '.'
while (tok->t < tok->max_t && isdigit((unsigned char)*tok->t)) tok->t++;
if (tok->t < tok->max_t && (*tok->t == 'e' || *tok->t == 'E')) {
tok->t++;
if (tok->t < tok->max_t && (*tok->t == '+' || *tok->t == '-')) tok->t++;
while (tok->t < tok->max_t && isdigit((unsigned char)*tok->t)) tok->t++;
}
token.len = (S32)(tok->t - tok->start_t);
return token;
}
static Token lua_parse_string(Tokenizer *tok, char quote) {
Token token;
token.start = (S32)(tok->start_t - tok->buf);
token.type = TOK_STRING_LITERAL;
B32 escape = 0;
tok->t++; // skip opening quote
while (tok->t < tok->max_t && *tok->t != '\n') {
if (*tok->t == quote && !escape) { tok->t++; break; }
escape = !escape && (*tok->t == '\\');
tok->t++;
}
token.len = (S32)(tok->t - tok->start_t);
return token;
}
static Token lua_parse_long_string(Tokenizer *tok, S32 level) {
Token token;
token.start = (S32)(tok->start_t - tok->buf);
token.type = TOK_STRING_LITERAL;
// Skip past opening [=*[
tok->t++; // first [
tok->t += level; // = signs
tok->t++; // second [
lua_scan_long_bracket_close(tok, level);
token.len = (S32)(tok->t - tok->start_t);
return token;
}
static Token lua_parse_comment(Tokenizer *tok) {
Token token;
token.start = (S32)(tok->start_t - tok->buf);
tok->t += 2; // skip '--'
// Check for long comment --[=*[
S32 level = lua_long_bracket_level(tok->t, tok->max_t);
if (level >= 0) {
token.type = TOK_MULTILINE_COMMENT;
tok->t++; // first [
tok->t += level; // = signs
tok->t++; // second [
lua_scan_long_bracket_close(tok, level);
} else {
// Single-line comment
token.type = TOK_COMMENT;
while (tok->t < tok->max_t && *tok->t != '\n') tok->t++;
}
token.len = (S32)(tok->t - tok->start_t);
return token;
}
static Token lua_parse_operator(Tokenizer *tok) {
Token token;
token.start = (S32)(tok->start_t - tok->buf);
token.type = TOK_OPERATION;
char c = *tok->t;
tok->t++;
if (tok->t < tok->max_t) {
char n = *tok->t;
// Two-character operators
if ((c == '=' && n == '=') || (c == '~' && n == '=') ||
(c == '<' && n == '=') || (c == '>' && n == '=') ||
(c == '.' && n == '.') || (c == '<' && n == '<') ||
(c == '>' && n == '>') || (c == '/' && n == '/')) {
tok->t++;
// Three-character: .. can be followed by . to make ...
if (c == '.' && n == '.' && tok->t < tok->max_t && *tok->t == '.') {
tok->t++;
}
}
}
token.len = (S32)(tok->t - tok->start_t);
return token;
}
////////////////////////////////
// Main get_next_token
static Token lua_get_next_token(Tokenizer *tok) {
tokenizer_eat_whitespace(tok);
Token token = {0};
token.start = (S32)(tok->t - tok->buf);
token.type = TOK_DEFAULT;
if (tok->t >= tok->max_t) {
token.len = 0;
return token;
}
tok->start_t = tok->t;
char c = *tok->t;
if (lua_is_ident_start(c)) {
return lua_parse_identifier(tok);
}
if (isdigit((unsigned char)c)) {
return lua_parse_number(tok);
}
switch (c) {
case '\'':
case '"':
return lua_parse_string(tok, c);
case '[': {
// Check for long string [=*[
S32 level = lua_long_bracket_level(tok->t, tok->max_t);
if (level >= 0) {
return lua_parse_long_string(tok, level);
}
// Plain bracket punctuation
token.type = TOK_PUNCTUATION;
tok->t++;
token.len = 1;
return token;
}
case '-':
// Check for comment --
if ((tok->t + 1) < tok->max_t && *(tok->t + 1) == '-') {
return lua_parse_comment(tok);
}
// Minus operator
token.type = TOK_OPERATION;
tok->t++;
token.len = 1;
return token;
case '.':
// .digits = float literal
if ((tok->t + 1) < tok->max_t && isdigit((unsigned char)*(tok->t + 1))) {
return lua_parse_dot_number(tok);
}
// .. or ... or just .
return lua_parse_operator(tok);
// Punctuation
case ';': case ',': case ':':
case '{': case '}': case '(': case ')': case ']':
token.type = TOK_PUNCTUATION;
tok->t++;
token.len = 1;
return token;
// Directive: shebang or labels (::label::)
case '#':
// Shebang line or length operator
if (tok->t == tok->buf && (tok->t + 1) < tok->max_t && *(tok->t + 1) == '!') {
token.type = TOK_DIRECTIVE;
while (tok->t < tok->max_t && *tok->t != '\n') tok->t++;
token.len = (S32)(tok->t - tok->start_t);
return token;
}
token.type = TOK_OPERATION;
tok->t++;
token.len = 1;
return token;
// Operators
case '=': case '~': case '<': case '>':
case '+': case '*': case '/': case '%':
case '^': case '&': case '|':
return lua_parse_operator(tok);
default:
token.type = TOK_INVALID;
tok->t++;
token.len = 1;
return token;
}
}
////////////////////////////////
// tokenize_lua -- main entry point
static void tokenize_lua(const char *data, S32 length, U8 *out_tokens) {
memset(out_tokens, TOK_DEFAULT, length);
Tokenizer tok;
tokenizer_init(&tok, data, length);
Token prev = {0};
prev.type = TOK_DEFAULT;
while (tok.t < tok.max_t) {
Token token = lua_get_next_token(&tok);
if (token.len == 0) break;
paint_token(out_tokens, token.start, token.len, token.type);
// Retroactively mark identifier before '(' as a function
if (token.type == TOK_PUNCTUATION && token.len == 1 &&
data[token.start] == '(' && prev.type == TOK_IDENTIFIER) {
paint_token(out_tokens, prev.start, prev.len, TOK_FUNCTION);
}
prev = token;
}
}