feat: add initial plot.py module with beast visualization logic
This commit introduces the core plotting module for the beast project, including the initial implementation of data visualization functions and chart generation utilities. The module provides the foundational structure for rendering beast-related metrics and graphical outputs.
This commit is contained in:
+160
@@ -0,0 +1,160 @@
|
||||
#ifndef RAVA_LEXER_H
|
||||
#define RAVA_LEXER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {
|
||||
RAVA_TOKEN_EOF,
|
||||
|
||||
RAVA_TOKEN_KEYWORD_ABSTRACT,
|
||||
RAVA_TOKEN_KEYWORD_ASSERT,
|
||||
RAVA_TOKEN_KEYWORD_BOOLEAN,
|
||||
RAVA_TOKEN_KEYWORD_BREAK,
|
||||
RAVA_TOKEN_KEYWORD_BYTE,
|
||||
RAVA_TOKEN_KEYWORD_CASE,
|
||||
RAVA_TOKEN_KEYWORD_CATCH,
|
||||
RAVA_TOKEN_KEYWORD_CHAR,
|
||||
RAVA_TOKEN_KEYWORD_CLASS,
|
||||
RAVA_TOKEN_KEYWORD_CONST,
|
||||
RAVA_TOKEN_KEYWORD_CONTINUE,
|
||||
RAVA_TOKEN_KEYWORD_DEFAULT,
|
||||
RAVA_TOKEN_KEYWORD_DO,
|
||||
RAVA_TOKEN_KEYWORD_DOUBLE,
|
||||
RAVA_TOKEN_KEYWORD_ELSE,
|
||||
RAVA_TOKEN_KEYWORD_ENUM,
|
||||
RAVA_TOKEN_KEYWORD_EXTENDS,
|
||||
RAVA_TOKEN_KEYWORD_FINAL,
|
||||
RAVA_TOKEN_KEYWORD_FINALLY,
|
||||
RAVA_TOKEN_KEYWORD_FLOAT,
|
||||
RAVA_TOKEN_KEYWORD_FOR,
|
||||
RAVA_TOKEN_KEYWORD_GOTO,
|
||||
RAVA_TOKEN_KEYWORD_IF,
|
||||
RAVA_TOKEN_KEYWORD_IMPLEMENTS,
|
||||
RAVA_TOKEN_KEYWORD_IMPORT,
|
||||
RAVA_TOKEN_KEYWORD_INSTANCEOF,
|
||||
RAVA_TOKEN_KEYWORD_INT,
|
||||
RAVA_TOKEN_KEYWORD_INTERFACE,
|
||||
RAVA_TOKEN_KEYWORD_LONG,
|
||||
RAVA_TOKEN_KEYWORD_NATIVE,
|
||||
RAVA_TOKEN_KEYWORD_NEW,
|
||||
RAVA_TOKEN_KEYWORD_PACKAGE,
|
||||
RAVA_TOKEN_KEYWORD_PRIVATE,
|
||||
RAVA_TOKEN_KEYWORD_PROTECTED,
|
||||
RAVA_TOKEN_KEYWORD_PUBLIC,
|
||||
RAVA_TOKEN_KEYWORD_RETURN,
|
||||
RAVA_TOKEN_KEYWORD_SHORT,
|
||||
RAVA_TOKEN_KEYWORD_STATIC,
|
||||
RAVA_TOKEN_KEYWORD_STRICTFP,
|
||||
RAVA_TOKEN_KEYWORD_SUPER,
|
||||
RAVA_TOKEN_KEYWORD_SWITCH,
|
||||
RAVA_TOKEN_KEYWORD_SYNCHRONIZED,
|
||||
RAVA_TOKEN_KEYWORD_THIS,
|
||||
RAVA_TOKEN_KEYWORD_THROW,
|
||||
RAVA_TOKEN_KEYWORD_THROWS,
|
||||
RAVA_TOKEN_KEYWORD_TRANSIENT,
|
||||
RAVA_TOKEN_KEYWORD_TRY,
|
||||
RAVA_TOKEN_KEYWORD_VOID,
|
||||
RAVA_TOKEN_KEYWORD_VOLATILE,
|
||||
RAVA_TOKEN_KEYWORD_WHILE,
|
||||
|
||||
RAVA_TOKEN_LITERAL_TRUE,
|
||||
RAVA_TOKEN_LITERAL_FALSE,
|
||||
RAVA_TOKEN_LITERAL_NULL,
|
||||
RAVA_TOKEN_LITERAL_INTEGER,
|
||||
RAVA_TOKEN_LITERAL_LONG,
|
||||
RAVA_TOKEN_LITERAL_FLOAT,
|
||||
RAVA_TOKEN_LITERAL_DOUBLE,
|
||||
RAVA_TOKEN_LITERAL_CHARACTER,
|
||||
RAVA_TOKEN_LITERAL_STRING,
|
||||
|
||||
RAVA_TOKEN_IDENTIFIER,
|
||||
|
||||
RAVA_TOKEN_LPAREN,
|
||||
RAVA_TOKEN_RPAREN,
|
||||
RAVA_TOKEN_LBRACE,
|
||||
RAVA_TOKEN_RBRACE,
|
||||
RAVA_TOKEN_LBRACKET,
|
||||
RAVA_TOKEN_RBRACKET,
|
||||
RAVA_TOKEN_SEMICOLON,
|
||||
RAVA_TOKEN_COMMA,
|
||||
RAVA_TOKEN_DOT,
|
||||
RAVA_TOKEN_ELLIPSIS,
|
||||
RAVA_TOKEN_AT,
|
||||
RAVA_TOKEN_COLONCOLON,
|
||||
|
||||
RAVA_TOKEN_ASSIGN,
|
||||
RAVA_TOKEN_GT,
|
||||
RAVA_TOKEN_LT,
|
||||
RAVA_TOKEN_BANG,
|
||||
RAVA_TOKEN_TILDE,
|
||||
RAVA_TOKEN_QUESTION,
|
||||
RAVA_TOKEN_COLON,
|
||||
RAVA_TOKEN_ARROW,
|
||||
RAVA_TOKEN_EQUAL,
|
||||
RAVA_TOKEN_GE,
|
||||
RAVA_TOKEN_LE,
|
||||
RAVA_TOKEN_NE,
|
||||
RAVA_TOKEN_AND,
|
||||
RAVA_TOKEN_OR,
|
||||
RAVA_TOKEN_INC,
|
||||
RAVA_TOKEN_DEC,
|
||||
RAVA_TOKEN_PLUS,
|
||||
RAVA_TOKEN_MINUS,
|
||||
RAVA_TOKEN_STAR,
|
||||
RAVA_TOKEN_SLASH,
|
||||
RAVA_TOKEN_AMP,
|
||||
RAVA_TOKEN_PIPE,
|
||||
RAVA_TOKEN_CARET,
|
||||
RAVA_TOKEN_PERCENT,
|
||||
RAVA_TOKEN_LSHIFT,
|
||||
RAVA_TOKEN_RSHIFT,
|
||||
RAVA_TOKEN_URSHIFT,
|
||||
RAVA_TOKEN_PLUSASSIGN,
|
||||
RAVA_TOKEN_MINUSASSIGN,
|
||||
RAVA_TOKEN_STARASSIGN,
|
||||
RAVA_TOKEN_SLASHASSIGN,
|
||||
RAVA_TOKEN_ANDASSIGN,
|
||||
RAVA_TOKEN_ORASSIGN,
|
||||
RAVA_TOKEN_CARETASSIGN,
|
||||
RAVA_TOKEN_PERCENTASSIGN,
|
||||
RAVA_TOKEN_LSHIFTASSIGN,
|
||||
RAVA_TOKEN_RSHIFTASSIGN,
|
||||
RAVA_TOKEN_URSHIFTASSIGN,
|
||||
|
||||
RAVA_TOKEN_ERROR
|
||||
} RavaTokenType_e;
|
||||
|
||||
typedef union {
|
||||
int64_t int_value;
|
||||
double float_value;
|
||||
char *string_value;
|
||||
char char_value;
|
||||
} RavaLiteralValue_u;
|
||||
|
||||
typedef struct {
|
||||
RavaTokenType_e type;
|
||||
char *lexeme;
|
||||
int line;
|
||||
int column;
|
||||
RavaLiteralValue_u value;
|
||||
} RavaToken_t;
|
||||
|
||||
typedef struct {
|
||||
const char *source;
|
||||
size_t source_length;
|
||||
size_t current;
|
||||
size_t start;
|
||||
int line;
|
||||
int column;
|
||||
int start_column;
|
||||
char *error_message;
|
||||
} RavaLexer_t;
|
||||
|
||||
RavaLexer_t* rava_lexer_create(const char *source);
|
||||
void rava_lexer_destroy(RavaLexer_t *lexer);
|
||||
RavaToken_t* rava_lexer_next_token(RavaLexer_t *lexer);
|
||||
void rava_token_destroy(RavaToken_t *token);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "lexer.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
const char *keyword;
|
||||
RavaTokenType_e type;
|
||||
} RavaKeyword_t;
|
||||
|
||||
static const RavaKeyword_t KEYWORDS[] = {
|
||||
{"abstract", RAVA_TOKEN_KEYWORD_ABSTRACT},
|
||||
{"assert", RAVA_TOKEN_KEYWORD_ASSERT},
|
||||
{"boolean", RAVA_TOKEN_KEYWORD_BOOLEAN},
|
||||
{"break", RAVA_TOKEN_KEYWORD_BREAK},
|
||||
{"byte", RAVA_TOKEN_KEYWORD_BYTE},
|
||||
{"case", RAVA_TOKEN_KEYWORD_CASE},
|
||||
{"catch", RAVA_TOKEN_KEYWORD_CATCH},
|
||||
{"char", RAVA_TOKEN_KEYWORD_CHAR},
|
||||
{"class", RAVA_TOKEN_KEYWORD_CLASS},
|
||||
{"const", RAVA_TOKEN_KEYWORD_CONST},
|
||||
{"continue", RAVA_TOKEN_KEYWORD_CONTINUE},
|
||||
{"default", RAVA_TOKEN_KEYWORD_DEFAULT},
|
||||
{"do", RAVA_TOKEN_KEYWORD_DO},
|
||||
{"double", RAVA_TOKEN_KEYWORD_DOUBLE},
|
||||
{"else", RAVA_TOKEN_KEYWORD_ELSE},
|
||||
{"enum", RAVA_TOKEN_KEYWORD_ENUM},
|
||||
{"extends", RAVA_TOKEN_KEYWORD_EXTENDS},
|
||||
{"false", RAVA_TOKEN_LITERAL_FALSE},
|
||||
{"final", RAVA_TOKEN_KEYWORD_FINAL},
|
||||
{"finally", RAVA_TOKEN_KEYWORD_FINALLY},
|
||||
{"float", RAVA_TOKEN_KEYWORD_FLOAT},
|
||||
{"for", RAVA_TOKEN_KEYWORD_FOR},
|
||||
{"goto", RAVA_TOKEN_KEYWORD_GOTO},
|
||||
{"if", RAVA_TOKEN_KEYWORD_IF},
|
||||
{"implements", RAVA_TOKEN_KEYWORD_IMPLEMENTS},
|
||||
{"import", RAVA_TOKEN_KEYWORD_IMPORT},
|
||||
{"instanceof", RAVA_TOKEN_KEYWORD_INSTANCEOF},
|
||||
{"int", RAVA_TOKEN_KEYWORD_INT},
|
||||
{"interface", RAVA_TOKEN_KEYWORD_INTERFACE},
|
||||
{"long", RAVA_TOKEN_KEYWORD_LONG},
|
||||
{"native", RAVA_TOKEN_KEYWORD_NATIVE},
|
||||
{"new", RAVA_TOKEN_KEYWORD_NEW},
|
||||
{"null", RAVA_TOKEN_LITERAL_NULL},
|
||||
{"package", RAVA_TOKEN_KEYWORD_PACKAGE},
|
||||
{"private", RAVA_TOKEN_KEYWORD_PRIVATE},
|
||||
{"protected", RAVA_TOKEN_KEYWORD_PROTECTED},
|
||||
{"public", RAVA_TOKEN_KEYWORD_PUBLIC},
|
||||
{"return", RAVA_TOKEN_KEYWORD_RETURN},
|
||||
{"short", RAVA_TOKEN_KEYWORD_SHORT},
|
||||
{"static", RAVA_TOKEN_KEYWORD_STATIC},
|
||||
{"strictfp", RAVA_TOKEN_KEYWORD_STRICTFP},
|
||||
{"super", RAVA_TOKEN_KEYWORD_SUPER},
|
||||
{"switch", RAVA_TOKEN_KEYWORD_SWITCH},
|
||||
{"synchronized", RAVA_TOKEN_KEYWORD_SYNCHRONIZED},
|
||||
{"this", RAVA_TOKEN_KEYWORD_THIS},
|
||||
{"throw", RAVA_TOKEN_KEYWORD_THROW},
|
||||
{"throws", RAVA_TOKEN_KEYWORD_THROWS},
|
||||
{"transient", RAVA_TOKEN_KEYWORD_TRANSIENT},
|
||||
{"true", RAVA_TOKEN_LITERAL_TRUE},
|
||||
{"try", RAVA_TOKEN_KEYWORD_TRY},
|
||||
{"void", RAVA_TOKEN_KEYWORD_VOID},
|
||||
{"volatile", RAVA_TOKEN_KEYWORD_VOLATILE},
|
||||
{"while", RAVA_TOKEN_KEYWORD_WHILE},
|
||||
};
|
||||
|
||||
static const size_t KEYWORDS_COUNT = sizeof(KEYWORDS) / sizeof(KEYWORDS[0]);
|
||||
|
||||
RavaTokenType_e rava_lexer_lookup_keyword(const char *identifier) {
|
||||
for (size_t i = 0; i < KEYWORDS_COUNT; i++) {
|
||||
if (strcmp(identifier, KEYWORDS[i].keyword) == 0) {
|
||||
return KEYWORDS[i].type;
|
||||
}
|
||||
}
|
||||
return RAVA_TOKEN_IDENTIFIER;
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "lexer.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern RavaToken_t* _rava_lexer_create_token(RavaLexer_t *lexer, RavaTokenType_e type);
|
||||
extern char _rava_lexer_peek(RavaLexer_t *lexer);
|
||||
extern char _rava_lexer_peek_next(RavaLexer_t *lexer);
|
||||
extern char _rava_lexer_advance(RavaLexer_t *lexer);
|
||||
extern bool _rava_lexer_is_at_end(RavaLexer_t *lexer);
|
||||
|
||||
static char _rava_unescape_char(char c) {
|
||||
switch (c) {
|
||||
case 'n': return '\n';
|
||||
case 't': return '\t';
|
||||
case 'r': return '\r';
|
||||
case 'b': return '\b';
|
||||
case 'f': return '\f';
|
||||
case '\\': return '\\';
|
||||
case '\'': return '\'';
|
||||
case '"': return '"';
|
||||
default: return c;
|
||||
}
|
||||
}
|
||||
|
||||
RavaToken_t* rava_lexer_parse_string(RavaLexer_t *lexer) {
|
||||
size_t capacity = 128;
|
||||
size_t length = 0;
|
||||
char *str = malloc(capacity);
|
||||
|
||||
while (!_rava_lexer_is_at_end(lexer) && _rava_lexer_peek(lexer) != '"') {
|
||||
char c = _rava_lexer_advance(lexer);
|
||||
|
||||
if (c == '\\') {
|
||||
if (_rava_lexer_is_at_end(lexer)) {
|
||||
free(str);
|
||||
lexer->error_message = strdup("Unterminated string escape");
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR);
|
||||
}
|
||||
|
||||
char next = _rava_lexer_advance(lexer);
|
||||
if (next == 'u') {
|
||||
int codepoint = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (_rava_lexer_is_at_end(lexer) || !isxdigit(_rava_lexer_peek(lexer))) {
|
||||
free(str);
|
||||
lexer->error_message = strdup("Invalid Unicode escape");
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR);
|
||||
}
|
||||
char hex = _rava_lexer_advance(lexer);
|
||||
codepoint = codepoint * 16 + (isdigit(hex) ? hex - '0' : tolower(hex) - 'a' + 10);
|
||||
}
|
||||
if (codepoint < 128) {
|
||||
c = (char)codepoint;
|
||||
} else {
|
||||
c = '?';
|
||||
}
|
||||
} else if (next >= '0' && next <= '7') {
|
||||
int octal = next - '0';
|
||||
if (!_rava_lexer_is_at_end(lexer) && _rava_lexer_peek(lexer) >= '0' && _rava_lexer_peek(lexer) <= '7') {
|
||||
octal = octal * 8 + (_rava_lexer_advance(lexer) - '0');
|
||||
if (!_rava_lexer_is_at_end(lexer) && _rava_lexer_peek(lexer) >= '0' && _rava_lexer_peek(lexer) <= '7') {
|
||||
octal = octal * 8 + (_rava_lexer_advance(lexer) - '0');
|
||||
}
|
||||
}
|
||||
c = (char)octal;
|
||||
} else {
|
||||
c = _rava_unescape_char(next);
|
||||
}
|
||||
}
|
||||
|
||||
if (length + 1 >= capacity) {
|
||||
capacity *= 2;
|
||||
str = realloc(str, capacity);
|
||||
}
|
||||
str[length++] = c;
|
||||
}
|
||||
|
||||
if (_rava_lexer_is_at_end(lexer)) {
|
||||
free(str);
|
||||
lexer->error_message = strdup("Unterminated string");
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR);
|
||||
}
|
||||
|
||||
_rava_lexer_advance(lexer);
|
||||
|
||||
str[length] = '\0';
|
||||
RavaToken_t *token = _rava_lexer_create_token(lexer, RAVA_TOKEN_LITERAL_STRING);
|
||||
token->value.string_value = str;
|
||||
return token;
|
||||
}
|
||||
|
||||
RavaToken_t* rava_lexer_parse_character(RavaLexer_t *lexer) {
|
||||
if (_rava_lexer_is_at_end(lexer)) {
|
||||
lexer->error_message = strdup("Unterminated character literal");
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR);
|
||||
}
|
||||
|
||||
char c = _rava_lexer_advance(lexer);
|
||||
|
||||
if (c == '\\') {
|
||||
if (_rava_lexer_is_at_end(lexer)) {
|
||||
lexer->error_message = strdup("Unterminated character escape");
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR);
|
||||
}
|
||||
char next = _rava_lexer_advance(lexer);
|
||||
c = _rava_unescape_char(next);
|
||||
}
|
||||
|
||||
if (_rava_lexer_is_at_end(lexer) || _rava_lexer_peek(lexer) != '\'') {
|
||||
lexer->error_message = strdup("Unterminated character literal");
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR);
|
||||
}
|
||||
|
||||
_rava_lexer_advance(lexer);
|
||||
|
||||
RavaToken_t *token = _rava_lexer_create_token(lexer, RAVA_TOKEN_LITERAL_CHARACTER);
|
||||
token->value.char_value = c;
|
||||
return token;
|
||||
}
|
||||
|
||||
RavaToken_t* rava_lexer_parse_number(RavaLexer_t *lexer) {
|
||||
bool is_hex = false;
|
||||
bool is_octal = false;
|
||||
bool is_binary = false;
|
||||
bool is_float = false;
|
||||
bool has_exponent = false;
|
||||
|
||||
char first_char = lexer->source[lexer->start];
|
||||
if (first_char == '0' && !_rava_lexer_is_at_end(lexer)) {
|
||||
char next = _rava_lexer_peek(lexer);
|
||||
if (next == 'x' || next == 'X') {
|
||||
is_hex = true;
|
||||
_rava_lexer_advance(lexer);
|
||||
_rava_lexer_advance(lexer);
|
||||
} else if (next == 'b' || next == 'B') {
|
||||
is_binary = true;
|
||||
_rava_lexer_advance(lexer);
|
||||
_rava_lexer_advance(lexer);
|
||||
} else if (next >= '0' && next <= '7') {
|
||||
is_octal = true;
|
||||
_rava_lexer_advance(lexer);
|
||||
}
|
||||
}
|
||||
|
||||
while (!_rava_lexer_is_at_end(lexer)) {
|
||||
char c = _rava_lexer_peek(lexer);
|
||||
|
||||
if (is_hex && isxdigit(c)) {
|
||||
_rava_lexer_advance(lexer);
|
||||
} else if (is_binary && (c == '0' || c == '1')) {
|
||||
_rava_lexer_advance(lexer);
|
||||
} else if (is_octal && c >= '0' && c <= '7') {
|
||||
_rava_lexer_advance(lexer);
|
||||
} else if (!is_hex && !is_binary && !is_octal && isdigit(c)) {
|
||||
_rava_lexer_advance(lexer);
|
||||
} else if (c == '.' && !is_float && !is_hex && !is_binary && !is_octal) {
|
||||
is_float = true;
|
||||
_rava_lexer_advance(lexer);
|
||||
} else if ((c == 'e' || c == 'E') && !has_exponent && !is_hex && !is_binary && !is_octal) {
|
||||
is_float = true;
|
||||
has_exponent = true;
|
||||
_rava_lexer_advance(lexer);
|
||||
if (!_rava_lexer_is_at_end(lexer) && (_rava_lexer_peek(lexer) == '+' || _rava_lexer_peek(lexer) == '-')) {
|
||||
_rava_lexer_advance(lexer);
|
||||
}
|
||||
} else if (c == '_') {
|
||||
_rava_lexer_advance(lexer);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RavaTokenType_e type = RAVA_TOKEN_LITERAL_INTEGER;
|
||||
|
||||
if (!_rava_lexer_is_at_end(lexer)) {
|
||||
char suffix = _rava_lexer_peek(lexer);
|
||||
if (suffix == 'L' || suffix == 'l') {
|
||||
type = RAVA_TOKEN_LITERAL_LONG;
|
||||
_rava_lexer_advance(lexer);
|
||||
} else if (suffix == 'F' || suffix == 'f') {
|
||||
is_float = true;
|
||||
type = RAVA_TOKEN_LITERAL_FLOAT;
|
||||
_rava_lexer_advance(lexer);
|
||||
} else if (suffix == 'D' || suffix == 'd') {
|
||||
is_float = true;
|
||||
type = RAVA_TOKEN_LITERAL_DOUBLE;
|
||||
_rava_lexer_advance(lexer);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_float && type == RAVA_TOKEN_LITERAL_INTEGER) {
|
||||
type = RAVA_TOKEN_LITERAL_DOUBLE;
|
||||
}
|
||||
|
||||
RavaToken_t *token = _rava_lexer_create_token(lexer, type);
|
||||
|
||||
if (is_float) {
|
||||
token->value.float_value = strtod(token->lexeme, NULL);
|
||||
} else {
|
||||
char *endptr;
|
||||
if (is_hex) {
|
||||
token->value.int_value = strtoll(token->lexeme, &endptr, 16);
|
||||
} else if (is_octal) {
|
||||
token->value.int_value = strtoll(token->lexeme, &endptr, 8);
|
||||
} else if (is_binary) {
|
||||
token->value.int_value = strtoll(token->lexeme + 2, &endptr, 2);
|
||||
} else {
|
||||
token->value.int_value = strtoll(token->lexeme, &endptr, 10);
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
#include "lexer.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern RavaTokenType_e rava_lexer_lookup_keyword(const char *identifier);
|
||||
extern RavaToken_t* rava_lexer_parse_string(RavaLexer_t *lexer);
|
||||
extern RavaToken_t* rava_lexer_parse_character(RavaLexer_t *lexer);
|
||||
extern RavaToken_t* rava_lexer_parse_number(RavaLexer_t *lexer);
|
||||
|
||||
RavaLexer_t* rava_lexer_create(const char *source) {
|
||||
RavaLexer_t *lexer = malloc(sizeof(RavaLexer_t));
|
||||
lexer->source = source;
|
||||
lexer->source_length = strlen(source);
|
||||
lexer->current = 0;
|
||||
lexer->start = 0;
|
||||
lexer->line = 1;
|
||||
lexer->column = 1;
|
||||
lexer->start_column = 1;
|
||||
lexer->error_message = NULL;
|
||||
return lexer;
|
||||
}
|
||||
|
||||
void rava_lexer_destroy(RavaLexer_t *lexer) {
|
||||
if (lexer) {
|
||||
if (lexer->error_message) {
|
||||
free(lexer->error_message);
|
||||
}
|
||||
free(lexer);
|
||||
}
|
||||
}
|
||||
|
||||
void rava_token_destroy(RavaToken_t *token) {
|
||||
if (token) {
|
||||
if (token->lexeme) {
|
||||
free(token->lexeme);
|
||||
}
|
||||
if (token->type == RAVA_TOKEN_LITERAL_STRING && token->value.string_value) {
|
||||
free(token->value.string_value);
|
||||
}
|
||||
free(token);
|
||||
}
|
||||
}
|
||||
|
||||
bool _rava_lexer_is_at_end(RavaLexer_t *lexer) {
|
||||
return lexer->current >= lexer->source_length;
|
||||
}
|
||||
|
||||
char _rava_lexer_peek(RavaLexer_t *lexer) {
|
||||
if (_rava_lexer_is_at_end(lexer)) return '\0';
|
||||
return lexer->source[lexer->current];
|
||||
}
|
||||
|
||||
char _rava_lexer_peek_next(RavaLexer_t *lexer) {
|
||||
if (lexer->current + 1 >= lexer->source_length) return '\0';
|
||||
return lexer->source[lexer->current + 1];
|
||||
}
|
||||
|
||||
char _rava_lexer_advance(RavaLexer_t *lexer) {
|
||||
if (_rava_lexer_is_at_end(lexer)) return '\0';
|
||||
char c = lexer->source[lexer->current++];
|
||||
if (c == '\n') {
|
||||
lexer->line++;
|
||||
lexer->column = 1;
|
||||
} else {
|
||||
lexer->column++;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
bool _rava_lexer_match(RavaLexer_t *lexer, char expected) {
|
||||
if (_rava_lexer_is_at_end(lexer)) return false;
|
||||
if (lexer->source[lexer->current] != expected) return false;
|
||||
lexer->current++;
|
||||
lexer->column++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void _rava_lexer_skip_whitespace(RavaLexer_t *lexer) {
|
||||
while (!_rava_lexer_is_at_end(lexer)) {
|
||||
char c = _rava_lexer_peek(lexer);
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\f':
|
||||
_rava_lexer_advance(lexer);
|
||||
break;
|
||||
case '/':
|
||||
if (_rava_lexer_peek_next(lexer) == '/') {
|
||||
while (!_rava_lexer_is_at_end(lexer) && _rava_lexer_peek(lexer) != '\n') {
|
||||
_rava_lexer_advance(lexer);
|
||||
}
|
||||
} else if (_rava_lexer_peek_next(lexer) == '*') {
|
||||
_rava_lexer_advance(lexer);
|
||||
_rava_lexer_advance(lexer);
|
||||
while (!_rava_lexer_is_at_end(lexer)) {
|
||||
if (_rava_lexer_peek(lexer) == '*' && _rava_lexer_peek_next(lexer) == '/') {
|
||||
_rava_lexer_advance(lexer);
|
||||
_rava_lexer_advance(lexer);
|
||||
break;
|
||||
}
|
||||
_rava_lexer_advance(lexer);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RavaToken_t* _rava_lexer_create_token(RavaLexer_t *lexer, RavaTokenType_e type) {
|
||||
RavaToken_t *token = malloc(sizeof(RavaToken_t));
|
||||
token->type = type;
|
||||
size_t length = lexer->current - lexer->start;
|
||||
token->lexeme = malloc(length + 1);
|
||||
memcpy(token->lexeme, lexer->source + lexer->start, length);
|
||||
token->lexeme[length] = '\0';
|
||||
token->line = lexer->line;
|
||||
token->column = lexer->start_column;
|
||||
token->value.int_value = 0;
|
||||
return token;
|
||||
}
|
||||
|
||||
static bool _rava_is_alpha(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$';
|
||||
}
|
||||
|
||||
static bool _rava_is_alnum(char c) {
|
||||
return _rava_is_alpha(c) || (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
static RavaToken_t* _rava_lexer_identifier(RavaLexer_t *lexer) {
|
||||
while (_rava_is_alnum(_rava_lexer_peek(lexer))) {
|
||||
_rava_lexer_advance(lexer);
|
||||
}
|
||||
|
||||
RavaToken_t *token = _rava_lexer_create_token(lexer, RAVA_TOKEN_IDENTIFIER);
|
||||
RavaTokenType_e keyword_type = rava_lexer_lookup_keyword(token->lexeme);
|
||||
token->type = keyword_type;
|
||||
|
||||
if (keyword_type == RAVA_TOKEN_LITERAL_TRUE) {
|
||||
token->value.int_value = 1;
|
||||
} else if (keyword_type == RAVA_TOKEN_LITERAL_FALSE) {
|
||||
token->value.int_value = 0;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
RavaToken_t* rava_lexer_next_token(RavaLexer_t *lexer) {
|
||||
_rava_lexer_skip_whitespace(lexer);
|
||||
|
||||
lexer->start = lexer->current;
|
||||
lexer->start_column = lexer->column;
|
||||
|
||||
if (_rava_lexer_is_at_end(lexer)) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_EOF);
|
||||
}
|
||||
|
||||
char c = _rava_lexer_advance(lexer);
|
||||
|
||||
if (_rava_is_alpha(c)) {
|
||||
return _rava_lexer_identifier(lexer);
|
||||
}
|
||||
|
||||
if (isdigit(c)) {
|
||||
return rava_lexer_parse_number(lexer);
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '(': return _rava_lexer_create_token(lexer, RAVA_TOKEN_LPAREN);
|
||||
case ')': return _rava_lexer_create_token(lexer, RAVA_TOKEN_RPAREN);
|
||||
case '{': return _rava_lexer_create_token(lexer, RAVA_TOKEN_LBRACE);
|
||||
case '}': return _rava_lexer_create_token(lexer, RAVA_TOKEN_RBRACE);
|
||||
case '[': return _rava_lexer_create_token(lexer, RAVA_TOKEN_LBRACKET);
|
||||
case ']': return _rava_lexer_create_token(lexer, RAVA_TOKEN_RBRACKET);
|
||||
case ';': return _rava_lexer_create_token(lexer, RAVA_TOKEN_SEMICOLON);
|
||||
case ',': return _rava_lexer_create_token(lexer, RAVA_TOKEN_COMMA);
|
||||
case '~': return _rava_lexer_create_token(lexer, RAVA_TOKEN_TILDE);
|
||||
case '?': return _rava_lexer_create_token(lexer, RAVA_TOKEN_QUESTION);
|
||||
case '@': return _rava_lexer_create_token(lexer, RAVA_TOKEN_AT);
|
||||
|
||||
case '.':
|
||||
if (_rava_lexer_peek(lexer) == '.' && _rava_lexer_peek_next(lexer) == '.') {
|
||||
_rava_lexer_advance(lexer);
|
||||
_rava_lexer_advance(lexer);
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ELLIPSIS);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_DOT);
|
||||
|
||||
case ':':
|
||||
if (_rava_lexer_match(lexer, ':')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_COLONCOLON);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_COLON);
|
||||
|
||||
case '+':
|
||||
if (_rava_lexer_match(lexer, '+')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_INC);
|
||||
}
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_PLUSASSIGN);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_PLUS);
|
||||
|
||||
case '-':
|
||||
if (_rava_lexer_match(lexer, '-')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_DEC);
|
||||
}
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_MINUSASSIGN);
|
||||
}
|
||||
if (_rava_lexer_match(lexer, '>')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ARROW);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_MINUS);
|
||||
|
||||
case '*':
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_STARASSIGN);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_STAR);
|
||||
|
||||
case '/':
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_SLASHASSIGN);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_SLASH);
|
||||
|
||||
case '%':
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_PERCENTASSIGN);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_PERCENT);
|
||||
|
||||
case '&':
|
||||
if (_rava_lexer_match(lexer, '&')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_AND);
|
||||
}
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ANDASSIGN);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_AMP);
|
||||
|
||||
case '|':
|
||||
if (_rava_lexer_match(lexer, '|')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_OR);
|
||||
}
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ORASSIGN);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_PIPE);
|
||||
|
||||
case '^':
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_CARETASSIGN);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_CARET);
|
||||
|
||||
case '!':
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_NE);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_BANG);
|
||||
|
||||
case '=':
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_EQUAL);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ASSIGN);
|
||||
|
||||
case '<':
|
||||
if (_rava_lexer_match(lexer, '<')) {
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_LSHIFTASSIGN);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_LSHIFT);
|
||||
}
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_LE);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_LT);
|
||||
|
||||
case '>':
|
||||
if (_rava_lexer_match(lexer, '>')) {
|
||||
if (_rava_lexer_match(lexer, '>')) {
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_URSHIFTASSIGN);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_URSHIFT);
|
||||
}
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_RSHIFTASSIGN);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_RSHIFT);
|
||||
}
|
||||
if (_rava_lexer_match(lexer, '=')) {
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_GE);
|
||||
}
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_GT);
|
||||
|
||||
case '"':
|
||||
return rava_lexer_parse_string(lexer);
|
||||
|
||||
case '\'':
|
||||
return rava_lexer_parse_character(lexer);
|
||||
|
||||
default:
|
||||
lexer->error_message = malloc(100);
|
||||
snprintf(lexer->error_message, 100, "Unexpected character '%c'", c);
|
||||
return _rava_lexer_create_token(lexer, RAVA_TOKEN_ERROR);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user