init
This commit is contained in:
commit
90edb5a86e
16
.clang-format
Normal file
16
.clang-format
Normal file
@ -0,0 +1,16 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: WebKit
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 80
|
||||
IndentCaseLabels: true
|
||||
InsertNewlineAtEOF: true
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
|
||||
BinPackArguments: false
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
|
||||
BinPackParameters: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
|
||||
# PointerAlignment: Right
|
||||
|
||||
6
compile_flags.txt
Normal file
6
compile_flags.txt
Normal file
@ -0,0 +1,6 @@
|
||||
-std=c23
|
||||
-pedantic-errors
|
||||
-Wall
|
||||
-Wextra
|
||||
|
||||
|
||||
111
lex.c
Normal file
111
lex.c
Normal file
@ -0,0 +1,111 @@
|
||||
#include "lex.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool done(Lexer* l)
|
||||
{
|
||||
return l->text[l->idx] != '\0';
|
||||
}
|
||||
|
||||
static Tok tok(Lexer* l, TokTy ty, Loc loc)
|
||||
{
|
||||
return (Tok) {
|
||||
.ty = ty,
|
||||
.loc = loc,
|
||||
.len = l->idx - loc.idx,
|
||||
};
|
||||
}
|
||||
|
||||
static void step(Lexer* l)
|
||||
{
|
||||
if (done(l)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->ch == '\n') {
|
||||
l->line += 1;
|
||||
l->col = 1;
|
||||
} else {
|
||||
l->col += 1;
|
||||
}
|
||||
l->idx += 1;
|
||||
l->ch = l->text[l->idx];
|
||||
}
|
||||
|
||||
static TokTy match_keyword(const char* text, size_t len)
|
||||
{
|
||||
if (strncmp("if", text, len))
|
||||
return Tok_If;
|
||||
return Tok_Ident;
|
||||
}
|
||||
|
||||
Tok lexer_next(Lexer* l)
|
||||
{
|
||||
Loc loc = { l->idx, l->line, l->col };
|
||||
if (done(l)) {
|
||||
return tok(l, Tok_Eof, loc);
|
||||
}
|
||||
bool matched = false;
|
||||
while (strchr(" \t\r", l->ch)) {
|
||||
matched = true;
|
||||
step(l);
|
||||
}
|
||||
if (matched) {
|
||||
return lexer_next(l);
|
||||
}
|
||||
while ((l->ch >= 'a' && l->ch <= 'z') || (l->ch >= 'A' && l->ch <= 'Z')
|
||||
|| (matched && l->ch >= '0' && l->ch <= '9') || strchr("_", l->ch)) {
|
||||
matched = true;
|
||||
step(l);
|
||||
}
|
||||
if (matched) {
|
||||
return tok(l, match_keyword(&l->text[loc.idx], l->idx - loc.idx), loc);
|
||||
}
|
||||
while ((l->ch >= '1' && l->ch <= '9') || (matched && l->ch == '0')) {
|
||||
matched = true;
|
||||
step(l);
|
||||
}
|
||||
if (matched) {
|
||||
return tok(l, Tok_Int, loc);
|
||||
}
|
||||
if (l->ch == '"') {
|
||||
step(l);
|
||||
while (l->ch != '"') {
|
||||
if (l->ch == '\\') {
|
||||
step(l);
|
||||
if (done(l))
|
||||
break;
|
||||
}
|
||||
step(l);
|
||||
}
|
||||
if (done(l) || l->ch != '"') {
|
||||
fprintf(stderr, "error: malformed string literal\n");
|
||||
return lexer_next(l);
|
||||
}
|
||||
step(l);
|
||||
return tok(l, Tok_Str, loc);
|
||||
}
|
||||
if (l->ch == '\'') {
|
||||
step(l);
|
||||
if (l->ch == '\\') {
|
||||
step(l);
|
||||
}
|
||||
if (!done(l)) {
|
||||
step(l);
|
||||
}
|
||||
if (done(l) && l->ch != '\'') {
|
||||
fprintf(stderr, "error: malformed character literal\n");
|
||||
return lexer_next(l);
|
||||
}
|
||||
step(l);
|
||||
return tok(l, Tok_Char, loc);
|
||||
}
|
||||
if (strchr("\n(){}", l->ch)) {
|
||||
TokTy ty = (TokTy)l->ch;
|
||||
step(l);
|
||||
return tok(l, ty, loc);
|
||||
}
|
||||
fprintf(stderr, "error: illegal character '%c'\n", l->ch);
|
||||
step(l);
|
||||
return lexer_next(l);
|
||||
}
|
||||
42
lex.h
Normal file
42
lex.h
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef LEX_H
|
||||
#define LEX_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
size_t idx;
|
||||
int line;
|
||||
int col;
|
||||
} Loc;
|
||||
|
||||
typedef enum {
|
||||
Tok_Eof,
|
||||
Tok_Ident,
|
||||
Tok_Int,
|
||||
Tok_Str,
|
||||
Tok_Char,
|
||||
Tok_If,
|
||||
Tok_Nl = '\n',
|
||||
Tok_LParen = '(',
|
||||
Tok_RParen = ')',
|
||||
Tok_LBrace = '{',
|
||||
Tok_RBrace = '}',
|
||||
} TokTy;
|
||||
|
||||
typedef struct {
|
||||
TokTy ty;
|
||||
Loc loc;
|
||||
size_t len;
|
||||
} Tok;
|
||||
|
||||
typedef struct {
|
||||
const char* text;
|
||||
size_t idx;
|
||||
int line;
|
||||
int col;
|
||||
char ch;
|
||||
} Lexer;
|
||||
|
||||
Tok lexer_next(Lexer* l);
|
||||
|
||||
#endif
|
||||
56
main.c
Normal file
56
main.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
char* read_textfile(const char* filename)
|
||||
{
|
||||
FILE* fp = fopen(filename, "r");
|
||||
if (!fp) {
|
||||
goto couldnt_read_file;
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_END) != 0) {
|
||||
goto couldnt_read_file;
|
||||
}
|
||||
|
||||
long file_size = ftell(fp);
|
||||
if (file_size < 0) {
|
||||
goto couldnt_read_file;
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_SET) != 0) {
|
||||
goto couldnt_read_file;
|
||||
}
|
||||
|
||||
char* buffer = malloc(file_size + 1);
|
||||
assert(buffer);
|
||||
|
||||
if (fread(buffer, sizeof(char), file_size, fp) != (size_t)file_size) {
|
||||
goto couldnt_read_file;
|
||||
}
|
||||
|
||||
buffer[file_size] = '\0';
|
||||
|
||||
couldnt_read_file:
|
||||
fprintf(stderr,
|
||||
"error: couldn't read file '%s': %s\n",
|
||||
filename,
|
||||
strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "error: no filename\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
char* filename = argv[1];
|
||||
char* text = read_textfile(filename);
|
||||
if (!text) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
64
parse.c
Normal file
64
parse.c
Normal file
@ -0,0 +1,64 @@
|
||||
#include "parse.h"
|
||||
#include <stddef.h>
|
||||
|
||||
// Using this as a reference:
|
||||
// https://en.cppreference.com/c/language/translation_phases
|
||||
|
||||
typedef struct {
|
||||
int line;
|
||||
int col;
|
||||
} Loc;
|
||||
|
||||
// Phase 1 already happens when the file is read.
|
||||
|
||||
typedef struct {
|
||||
Loc loc;
|
||||
char ch;
|
||||
} CannonicalChar;
|
||||
|
||||
typedef struct {
|
||||
const char* text;
|
||||
size_t idx;
|
||||
Loc loc;
|
||||
} NewlineCanonicalizer;
|
||||
|
||||
CannonicalChar next_cannonical_char(NewlineCanonicalizer* iter)
|
||||
{
|
||||
Loc loc = iter->loc;
|
||||
char ch = iter->text[iter->idx];
|
||||
if (ch == '\0')
|
||||
return (CannonicalChar) { loc, ch };
|
||||
do {
|
||||
if (ch == '\n') {
|
||||
|
||||
} else {
|
||||
}
|
||||
iter->loc.col += 1;
|
||||
|
||||
} while (iter->text[iter->idx]);
|
||||
}
|
||||
|
||||
// Phase 2 is an iterator that handles escaped newlines.
|
||||
|
||||
typedef struct {
|
||||
/// when this is '\0', it means EOF
|
||||
char ch;
|
||||
int line;
|
||||
int col;
|
||||
} Char;
|
||||
|
||||
typedef struct {
|
||||
const char* text;
|
||||
size_t idx;
|
||||
int line;
|
||||
int col;
|
||||
} CharIter;
|
||||
|
||||
static Char next_src_char(CharIter* iter)
|
||||
{
|
||||
if (!iter->text[iter->idx])
|
||||
return (Char) { .ch = '\0', iter->line, iter->col };
|
||||
char ch = iter->text[iter->idx];
|
||||
iter->idx += 1;
|
||||
while (ch == '\\' &&)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user