lexer
This commit is contained in:
parent
2f1b2b50d5
commit
ae098219b3
14
.clang-format
Normal file
14
.clang-format
Normal file
@ -0,0 +1,14 @@
|
||||
BasedOnStyle: WebKit
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 80
|
||||
IndentCaseLabels: true
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
SplitEmptyFunction: false
|
||||
AlignAfterOpenBracket: BlockIndent
|
||||
AlignOperands: AlignAfterOperator
|
||||
BreakBeforeBinaryOperators: true
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
matemateak
|
||||
*.o
|
||||
compile_flags.txt
|
||||
|
23
Makefile
Normal file
23
Makefile
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
CC = gcc
|
||||
|
||||
CFLAGS = -std=c17 -Wall -Wextra -Wpedantic -Wconversion
|
||||
LFLAGS = -lm
|
||||
|
||||
C_FILES = $(shell find src/ -name *.c)
|
||||
HEADER_FILES = $(shell find src/ -name *.h)
|
||||
|
||||
OBJECT_FILES = $(patsubst %.c, %.o, $(C_FILES))
|
||||
|
||||
matemateak: $(OBJECT_FILES)
|
||||
$(CC) -o $@ $(LFLAGS) $^
|
||||
|
||||
%.o: %.c $(HEADER_FILES)
|
||||
$(CC) -c -o $@ $(CFLAGS) $<
|
||||
|
||||
compile_flags.txt:
|
||||
echo -xc $(C_FLAGS) | sed 's/\s\+/\n/g' > compile_flags.txt
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJECT_FILES) matemateak
|
||||
|
123
src/lexer.c
Normal file
123
src/lexer.c
Normal file
@ -0,0 +1,123 @@
|
||||
#include "lexer.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
bool lexer_done(const Lexer* lexer) { return lexer->index >= lexer->length; }
|
||||
|
||||
char lexer_current(const Lexer* lexer) { return lexer->text[lexer->index]; }
|
||||
|
||||
void lexer_step(Lexer* lexer)
|
||||
{
|
||||
lexer->index += 1;
|
||||
if (!lexer_done(lexer)) {
|
||||
if (lexer_current(lexer) == '\n') {
|
||||
lexer->line += 1;
|
||||
lexer->col = 1;
|
||||
} else {
|
||||
lexer->col += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Position lexer_pos(const Lexer* lexer)
|
||||
{
|
||||
return (Position) {
|
||||
.index = lexer->index,
|
||||
.line = lexer->line,
|
||||
.col = lexer->col,
|
||||
};
|
||||
}
|
||||
|
||||
Token lexer_token(const Lexer* lexer, TokenType type, Position start)
|
||||
{
|
||||
return (Token) {
|
||||
.type = type,
|
||||
.index = start.index,
|
||||
.length = lexer->index - start.index,
|
||||
.line = start.line,
|
||||
.col = start.col,
|
||||
};
|
||||
}
|
||||
|
||||
Token lexer_step_and_token(Lexer* lexer, TokenType type, Position start)
|
||||
{
|
||||
lexer_step(lexer);
|
||||
return lexer_token(lexer, type, start);
|
||||
}
|
||||
|
||||
bool lexer_is_whitespace(char value)
|
||||
{
|
||||
return value == ' ' || value == '\t' || value == '\r' || value == '\n';
|
||||
}
|
||||
|
||||
Token lexer_skip_whitespace(Lexer* lexer)
|
||||
{
|
||||
lexer_step(lexer);
|
||||
while (!lexer_done(lexer) && lexer_is_whitespace(lexer_current(lexer)))
|
||||
lexer_step(lexer);
|
||||
return lexer_next(lexer);
|
||||
}
|
||||
|
||||
bool lexer_is_int(char value) { return value >= '0' && value <= '9'; }
|
||||
|
||||
bool lexer_is_int_start(char value) { return lexer_is_int(value); }
|
||||
|
||||
Token lexer_int_token(Lexer* lexer)
|
||||
{
|
||||
Position start = lexer_pos(lexer);
|
||||
lexer_step(lexer);
|
||||
while (!lexer_done(lexer) && lexer_is_int(lexer_current(lexer)))
|
||||
lexer_step(lexer);
|
||||
return lexer_token(lexer, TokenTypeInt, start);
|
||||
}
|
||||
|
||||
void lexer(Lexer* lexer, const char* text, size_t length)
|
||||
{
|
||||
*lexer = (Lexer) {
|
||||
.text = text,
|
||||
.length = length,
|
||||
.index = 0,
|
||||
.line = 1,
|
||||
.col = 1,
|
||||
};
|
||||
}
|
||||
|
||||
Token lexer_level3(Lexer* lexer)
|
||||
{
|
||||
if (lexer_is_int_start(lexer_current(lexer)))
|
||||
return lexer_int_token(lexer);
|
||||
else
|
||||
return lexer_step_and_token(
|
||||
lexer, TokenTypeInvalidChar, lexer_pos(lexer)
|
||||
);
|
||||
}
|
||||
|
||||
Token lexer_level2(Lexer* lexer)
|
||||
{
|
||||
Position start = lexer_pos(lexer);
|
||||
switch (lexer_current(lexer)) {
|
||||
case '+':
|
||||
return lexer_step_and_token(lexer, TokenTypePlus, start);
|
||||
case '-':
|
||||
return lexer_step_and_token(lexer, TokenTypeMinus, start);
|
||||
case '*':
|
||||
return lexer_step_and_token(lexer, TokenTypeAsterisk, start);
|
||||
case '/':
|
||||
return lexer_step_and_token(lexer, TokenTypeSlash, start);
|
||||
case '(':
|
||||
return lexer_step_and_token(lexer, TokenTypeLParen, start);
|
||||
case ')':
|
||||
return lexer_step_and_token(lexer, TokenTypeRParen, start);
|
||||
default:
|
||||
return lexer_level3(lexer);
|
||||
}
|
||||
}
|
||||
|
||||
Token lexer_next(Lexer* lexer)
|
||||
{
|
||||
if (lexer_done(lexer))
|
||||
return lexer_token(lexer, TokenTypeEof, lexer_pos(lexer));
|
||||
else if (lexer_is_whitespace(lexer_current(lexer)))
|
||||
return lexer_skip_whitespace(lexer);
|
||||
else
|
||||
return lexer_level2(lexer);
|
||||
}
|
38
src/lexer.h
Normal file
38
src/lexer.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef LEXER_H
|
||||
#define LEXER_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
size_t index;
|
||||
int line, col;
|
||||
} Position;
|
||||
|
||||
typedef enum {
|
||||
TokenTypeEof,
|
||||
TokenTypeInvalidChar,
|
||||
TokenTypeInt,
|
||||
TokenTypePlus,
|
||||
TokenTypeMinus,
|
||||
TokenTypeAsterisk,
|
||||
TokenTypeSlash,
|
||||
TokenTypeLParen,
|
||||
TokenTypeRParen,
|
||||
} TokenType;
|
||||
|
||||
typedef struct {
|
||||
TokenType type;
|
||||
size_t index, length;
|
||||
int line, col;
|
||||
} Token;
|
||||
|
||||
typedef struct {
|
||||
const char* text;
|
||||
size_t index, length;
|
||||
int line, col;
|
||||
} Lexer;
|
||||
|
||||
void lexer(Lexer* lexer, const char* text, size_t length);
|
||||
Token lexer_next(Lexer* lexer);
|
||||
|
||||
#endif
|
4
src/main.c
Normal file
4
src/main.c
Normal file
@ -0,0 +1,4 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void) { printf("hello world\n"); }
|
Loading…
Reference in New Issue
Block a user