Initial commit
This commit is contained in:
commit
5e1d6ae207
|
@ -0,0 +1,3 @@
|
|||
*.o
|
||||
*.txt
|
||||
readme
|
|
@ -0,0 +1,23 @@
|
|||
.PHONY: run test run clean
|
||||
|
||||
CC=gcc
|
||||
CFLAGS=-Wall -Wpedantic -Werror -std=c99
|
||||
|
||||
OBJ=main.o lexer.o token.o node.o
|
||||
|
||||
all: readme
|
||||
|
||||
run: readme
|
||||
./readme
|
||||
|
||||
readme: ${OBJ}
|
||||
${CC} ${CFLAGS} -o $@ $^
|
||||
|
||||
#token.o: token.h token.c
|
||||
# ${CC} ${CFLAGS} -o $@ $<
|
||||
|
||||
.c.o:
|
||||
${CC} ${CFLAGS} -c -o $@ $<
|
||||
|
||||
clean:
|
||||
-rm *.o readme
|
|
@ -0,0 +1,235 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lexer.h"
|
||||
|
||||
static void readChar(Lexer* l);
|
||||
static char* readIdentifier(Lexer* l);
|
||||
static char* readNumber(Lexer* l);
|
||||
static int isLetter(char c);
|
||||
static int isDigit(char c);
|
||||
|
||||
static Token* newToken(Lexer* l, TokenType tt);
|
||||
static Token* newIdentToken(Lexer* l, char* literal, TokenType tt);
|
||||
|
||||
Lexer*
|
||||
NewLexer(char* filename)
|
||||
{
|
||||
FILE* fp;
|
||||
fp = fopen(filename, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
{
|
||||
printf("Can't open the file for some reason\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
int fileSize = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
printf("fileSize: %d\n", fileSize);
|
||||
|
||||
Lexer* state = malloc(sizeof(Lexer));
|
||||
state->rawFile = malloc((sizeof(char) * fileSize) + 1);
|
||||
state->rawLen = fileSize;
|
||||
|
||||
size_t read = fread(state->rawFile, sizeof(char), fileSize, fp);
|
||||
if (read != fileSize)
|
||||
{
|
||||
printf("something borked. only read %d bytes of %d\n", (int)read, fileSize);
|
||||
|
||||
free(state->rawFile);
|
||||
free(state);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
state->rawFile[fileSize] = '\0';
|
||||
state->line = 1;
|
||||
|
||||
readChar(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
void
|
||||
FreeLexer(Lexer* l)
|
||||
{
|
||||
free(l->rawFile);
|
||||
free(l);
|
||||
}
|
||||
|
||||
Token*
|
||||
NextToken(Lexer* l)
|
||||
{
|
||||
Token* tok;
|
||||
switch (l->ch) {
|
||||
case '#':
|
||||
tok = newToken(l, TT_HASH);
|
||||
break;
|
||||
case '*':
|
||||
tok = newToken(l, TT_ASTERISK);
|
||||
break;
|
||||
case '_':
|
||||
tok = newToken(l, TT_UNDERSCORE);
|
||||
break;
|
||||
case '-':
|
||||
tok = newToken(l, TT_DASH);
|
||||
break;
|
||||
case '.':
|
||||
tok = newToken(l, TT_PERIOD);
|
||||
break;
|
||||
case '`':
|
||||
tok = newToken(l, TT_BACKTICK);
|
||||
break;
|
||||
case '\0':
|
||||
tok = newToken(l, TT_EOF);
|
||||
break;
|
||||
case '\n':
|
||||
tok = newToken(l, TT_NEWLINE);
|
||||
l->line++;
|
||||
l->column = 0;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
tok = newToken(l, TT_WHITESPACE);
|
||||
break;
|
||||
case '\r':
|
||||
readChar(l);
|
||||
return NextToken(l); // lets GOOOOO
|
||||
default:
|
||||
if (isLetter(l->ch))
|
||||
{
|
||||
int start = l->column;
|
||||
char* literal = readIdentifier(l);
|
||||
tok = newIdentToken(l, literal, TT_WORD);
|
||||
tok->column = start;
|
||||
return tok;
|
||||
}
|
||||
else if (isDigit(l->ch))
|
||||
{
|
||||
int start = l->column;
|
||||
char* literal = readNumber(l);
|
||||
tok = newIdentToken(l, literal, TT_NUMBER);
|
||||
tok->column = start;
|
||||
return tok;
|
||||
}
|
||||
else
|
||||
{
|
||||
tok = newToken(l, TT_ILLEGAL);
|
||||
}
|
||||
//printf("Invalid token: %X\n", l->ch);
|
||||
//return NULL;
|
||||
}
|
||||
|
||||
readChar(l);
|
||||
return tok;
|
||||
}
|
||||
|
||||
static
|
||||
char*
|
||||
readNumber(Lexer* l)
|
||||
{
|
||||
int position = l->position;
|
||||
while (isDigit(l->ch))
|
||||
{
|
||||
readChar(l);
|
||||
}
|
||||
|
||||
int len = (l->position - position);
|
||||
char* out = malloc(sizeof(char) * len + 1);
|
||||
memcpy(out, &l->rawFile[position], len);
|
||||
out[len] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
static
|
||||
char*
|
||||
readIdentifier(Lexer* l)
|
||||
{
|
||||
int position = l->position;
|
||||
while (isLetter(l->ch))
|
||||
{
|
||||
readChar(l);
|
||||
}
|
||||
|
||||
int len = (l->position - position);
|
||||
char* out = malloc(sizeof(char) * len + 1);
|
||||
memcpy(out, &l->rawFile[position], len);
|
||||
out[len] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void
|
||||
readChar(Lexer* l)
|
||||
{
|
||||
l->column++;
|
||||
if (l->readPosition >= l->rawLen)
|
||||
{
|
||||
l->ch = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
l->ch = l->rawFile[l->readPosition];
|
||||
}
|
||||
|
||||
l->position = l->readPosition;
|
||||
l->readPosition++;
|
||||
}
|
||||
|
||||
void
|
||||
Parse(Lexer* l)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
isLetter(char ch)
|
||||
{
|
||||
return (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '_');
|
||||
}
|
||||
|
||||
int
|
||||
isDigit(char ch)
|
||||
{
|
||||
return ('0' <= ch && ch <= '9');
|
||||
}
|
||||
|
||||
void
|
||||
FreeToken(Token* t)
|
||||
{
|
||||
free(t->literal);
|
||||
free(t);
|
||||
}
|
||||
|
||||
static
|
||||
Token*
|
||||
newToken(Lexer* l,
|
||||
TokenType tt)
|
||||
{
|
||||
Token* tok = malloc(sizeof(Token));
|
||||
char* nc = malloc(sizeof(char)+1);
|
||||
*nc = l->ch;
|
||||
nc[1] = '\0';
|
||||
tok->type = tt;
|
||||
tok->literal = nc;
|
||||
tok->line = l->line;
|
||||
tok->column = l->column;
|
||||
return tok;
|
||||
}
|
||||
|
||||
static
|
||||
Token*
|
||||
newIdentToken(Lexer* l,
|
||||
char* literal,
|
||||
TokenType tt)
|
||||
{
|
||||
Token* tok = malloc(sizeof(Token));
|
||||
tok->type = tt;
|
||||
tok->literal = literal;
|
||||
tok->line = l->line;
|
||||
tok->column = l->column;
|
||||
return tok;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
#include "token.h"
|
||||
|
||||
#ifndef LEXER_H
|
||||
#define LEXER_H
|
||||
|
||||
//typedef enum NodeType {
|
||||
// NT_Root,
|
||||
// NT_Header1,
|
||||
// NT_Header2,
|
||||
// NT_Header3,
|
||||
// NT_ListItem,
|
||||
// NT_OrderedListItem,
|
||||
// NT_Paragraph,
|
||||
// NT_PlainText,
|
||||
// NT_BoldText,
|
||||
// NT_UnderlineText,
|
||||
// NT_InlineCode,
|
||||
// NT_BlockCode,
|
||||
//} NodeType;
|
||||
|
||||
typedef struct Lexer {
|
||||
char* rawFile;
|
||||
int rawLen;
|
||||
int position; // current index
|
||||
int readPosition; // next index
|
||||
char ch; // character under examination
|
||||
|
||||
// values for current index
|
||||
int line;
|
||||
int column;
|
||||
|
||||
} Lexer;
|
||||
|
||||
//typedef struct Node {
|
||||
// NodeType type;
|
||||
// char RawText;
|
||||
// int LineNumber;
|
||||
//
|
||||
// //struct Node **ChildNodes;
|
||||
// void** ChildNodes;
|
||||
// int ChildCount;
|
||||
//} Node;
|
||||
|
||||
Lexer* NewLexer(char* filename);
|
||||
Token* NextToken(Lexer* l);
|
||||
void ReadChar(Lexer* l);
|
||||
void Parse(Lexer* l);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,99 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "token.h"
|
||||
#include "lexer.h"
|
||||
#include "node.h"
|
||||
|
||||
/*
|
||||
* RawText ""
|
||||
* LineNumber 0
|
||||
* NodeType NT_Root
|
||||
* ChildNodes
|
||||
* RawText "# Header1"
|
||||
* LineNumber 1
|
||||
* NodeType NT_Header1
|
||||
* ChildNodes
|
||||
* {"Some text."}
|
||||
*
|
||||
* RawText "## Header2"
|
||||
*/
|
||||
|
||||
/*
|
||||
* NodeType NT_Root
|
||||
* ChildNodes
|
||||
* RawText "## Header2"
|
||||
* ChildNodes
|
||||
* paragraph
|
||||
* ChildNodes
|
||||
* {*bold text*}
|
||||
* {_underlined text_}
|
||||
* paragraph
|
||||
*
|
||||
*
|
||||
*/
|
||||
//Node* ParseLine(char *buffer);
|
||||
|
||||
void writeTokenFile(TokenList* tl);
|
||||
|
||||
int
|
||||
main(int argc, const char** argv)
|
||||
{
|
||||
Lexer* l = NewLexer("sample.md");
|
||||
TokenList* current = malloc(sizeof(TokenList));
|
||||
TokenList* tl = current;//= malloc(sizeof(TokenList));
|
||||
current->token = NULL;
|
||||
|
||||
TokenType tt;
|
||||
do
|
||||
{
|
||||
Token* t = NextToken(l);
|
||||
tt = t->type;
|
||||
current = TokenListAdd(current, t);
|
||||
}
|
||||
while(tt != TT_EOF);
|
||||
|
||||
writeTokenFile(tl);
|
||||
ParseNodes(tl);
|
||||
|
||||
printf("rawLen: %d position: %d readPosition: %d ch: %c line: %d column: %d\n",
|
||||
l->rawLen,
|
||||
l->position,
|
||||
l->readPosition,
|
||||
l->ch,
|
||||
l->line,
|
||||
l->column
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
writeTokenFile(TokenList* tl)
|
||||
{
|
||||
int count;
|
||||
FILE* fp = fopen("tokens.txt", "w");
|
||||
if (fp == NULL)
|
||||
{
|
||||
printf("unable to open output.txt\n");
|
||||
return;
|
||||
}
|
||||
|
||||
TokenList* current = tl;
|
||||
for(count = 0; current->next != NULL; count++) {
|
||||
if (count == 0 && current->token == NULL)
|
||||
{
|
||||
printf("first token null\n");
|
||||
}
|
||||
else if (count == 0)
|
||||
{
|
||||
printf("%s\n", TokenString(current->token));
|
||||
}
|
||||
fprintf(fp, "%s\n", TokenString(current->token));
|
||||
current = current->next;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
printf("Token count: %d\n", count);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#define MAXBUFFER 1024
|
||||
|
||||
#endif
|
|
@ -0,0 +1,104 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "node.h"
|
||||
|
||||
#define STRING_BUFF_SIZE 1024
|
||||
|
||||
static char stringBuff[STRING_BUFF_SIZE];
|
||||
|
||||
Node* parseHeader(TokenList** list);
|
||||
|
||||
NodeList*
|
||||
ParseNodes(TokenList* list)
|
||||
{
|
||||
NodeList* nl = malloc(sizeof(NodeList));
|
||||
NodeList* currentNode = nl;
|
||||
|
||||
currentNode->next = NULL;
|
||||
currentNode->node = NULL;
|
||||
|
||||
TokenList* current = list;
|
||||
|
||||
//while(current != NULL) {
|
||||
while (1) {
|
||||
switch (current->token->type) {
|
||||
case TT_NEWLINE:
|
||||
break;
|
||||
case TT_HASH:
|
||||
// start of header
|
||||
//Node* nodes;
|
||||
//nodes = parseHeader(current);
|
||||
currentNode->node = parseHeader(¤t);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (current->next == NULL) {
|
||||
//printf("next is null\n");
|
||||
break;
|
||||
}
|
||||
//printf("current = current->next;\n");
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return nl;
|
||||
}
|
||||
|
||||
Node*
|
||||
parseHeader(TokenList** list)
|
||||
{
|
||||
TokenList* l = *list;
|
||||
// Count the number of TT_HASH tokens
|
||||
int count = 1;
|
||||
while (l->next != NULL && l->next->token->type == TT_HASH)
|
||||
{
|
||||
count++;
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
if (l->next == NULL)
|
||||
{
|
||||
printf("Header missing text");
|
||||
return NULL;
|
||||
}
|
||||
l = l->next;
|
||||
|
||||
// Trim leading whitespace
|
||||
while (l->next != NULL && l->token->type == TT_WHITESPACE)
|
||||
{
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
if (l->next == NULL)
|
||||
{
|
||||
printf("Header missing text");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stringBuff[0] = '\0';
|
||||
while (1)
|
||||
{
|
||||
int bufSize = strlen(stringBuff);
|
||||
int litSize = strlen(l->token->literal);
|
||||
if (bufSize + litSize + 1 > STRING_BUFF_SIZE)
|
||||
{
|
||||
printf("Buffer not big enough!");
|
||||
return NULL;
|
||||
}
|
||||
strncat(stringBuff, l->token->literal, strlen(l->token->literal));
|
||||
|
||||
if (l->next == NULL || l->next->token->type == TT_NEWLINE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
*list = l;
|
||||
printf("header hash count: %d\ntext: '%s'\n", count, stringBuff);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "token.h"
|
||||
|
||||
#ifndef NODE_H
|
||||
#define NODE_H
|
||||
|
||||
typedef enum {
|
||||
NT_Header1,
|
||||
NT_Header2,
|
||||
NT_Header3,
|
||||
NT_Header4,
|
||||
NT_Paragraph,
|
||||
NT_UnorderedList,
|
||||
NT_OrderedList,
|
||||
NT_InlineCode,
|
||||
NT_BlockCode,
|
||||
NT_BlockQuote,
|
||||
NT_Bold,
|
||||
NT_Underline,
|
||||
} NodeType;
|
||||
|
||||
struct NodeList;
|
||||
|
||||
typedef struct Node {
|
||||
NodeType type;
|
||||
struct NodeList* children;
|
||||
} Node;
|
||||
|
||||
typedef struct NodeList {
|
||||
struct Node* node;
|
||||
struct Node* next;
|
||||
} NodeList;
|
||||
|
||||
typedef struct {
|
||||
NodeType type;
|
||||
struct Node* next;
|
||||
char* rawText;
|
||||
} HeaderNode;
|
||||
|
||||
NodeList* ParseNodes(TokenList* list);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,28 @@
|
|||
# Terminal Markdown Viewer
|
||||
|
||||
## Goals
|
||||
|
||||
To render markdown in the terminal and use colors, font weights, etc, to
|
||||
display the document.
|
||||
|
||||
## Implemented syntax
|
||||
|
||||
- Headers
|
||||
- Unordered lists
|
||||
- Ordered lists
|
||||
- Inline code
|
||||
- Block code
|
||||
- Block quote?
|
||||
- Bold
|
||||
- Underline
|
||||
|
||||
### maybies
|
||||
|
||||
- Task list
|
||||
- Explicit colors
|
||||
- Inter-document links
|
||||
|
||||
### nopes
|
||||
|
||||
- Tables
|
||||
- Syntax highlighting code
|
|
@ -0,0 +1,46 @@
|
|||
# Header 1
|
||||
|
||||
Some text.
|
||||
|
||||
## Header 2
|
||||
|
||||
*bold text*
|
||||
_underlined text_
|
||||
|
||||
Nostra sem bibendum ridiculus aenean condimentum sed eleifend et odio egestas
|
||||
pellentesque. *Sit fusce.* At ligula dolor parturient sodales auctor. Egestas.
|
||||
|
||||
Dictum pharetra nulla _aliquet tincidunt_ parturient netus gravida rutrum
|
||||
rhoncus. Donec dis mollis ornare `bibendum sollicitudin` velit lectus inceptos.
|
||||
|
||||
```
|
||||
Laoreet arcu eget cubilia auctor vitae cursus lacus volutpat dui.
|
||||
```
|
||||
|
||||
### Header 3
|
||||
|
||||
- List item one.
|
||||
- List item two.
|
||||
- List item three.
|
||||
- List item four.
|
||||
|
||||
1. Ordered list one
|
||||
1. Ordered list two
|
||||
1. Ordered list three
|
||||
1. Ordered list four
|
||||
|
||||
- Toplevel one
|
||||
- Second level one
|
||||
- Second level two
|
||||
- Toplevel two
|
||||
- Second level one
|
||||
- Third level
|
||||
- Second level two
|
||||
|
||||
1. Ordered toplevel one
|
||||
1. Ordered second level one
|
||||
1. Ordered second level two
|
||||
1. Ordered toplevel two
|
||||
1. Ordered second level one
|
||||
1. Ordered third level
|
||||
1. Ordered second level two
|
|
@ -0,0 +1,99 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "token.h"
|
||||
|
||||
#define STRING_BUFF_SIZE 1024
|
||||
|
||||
static char stringBuff[STRING_BUFF_SIZE];
|
||||
|
||||
char* printableOnly(char* input);
|
||||
|
||||
TokenList*
|
||||
TokenListAdd(TokenList* current, Token* next)
|
||||
{
|
||||
if (current->token == NULL)
|
||||
{
|
||||
printf("current->token == null\n");
|
||||
current->token = next;
|
||||
return current;
|
||||
}
|
||||
|
||||
TokenList* nl = malloc(sizeof(TokenList));
|
||||
nl->token = next;
|
||||
current->next = nl;
|
||||
return nl;
|
||||
}
|
||||
|
||||
char*
|
||||
TokenString(Token* t)
|
||||
{
|
||||
//char* str = malloc(sizeof(char) * 1000);
|
||||
snprintf(stringBuff, 1000, "[%d:%d] Type: %s Literal: '%s'",
|
||||
t->line,
|
||||
t->column,
|
||||
TokenTypeString(t->type),
|
||||
printableOnly(t->literal)
|
||||
);
|
||||
|
||||
return stringBuff;
|
||||
}
|
||||
|
||||
char*
|
||||
TokenTypeString(TokenType tt)
|
||||
{
|
||||
switch (tt) {
|
||||
case TT_ILLEGAL:
|
||||
return "TT_ILLEGAL";
|
||||
case TT_EOF:
|
||||
return "TT_EOF";
|
||||
case TT_HASH:
|
||||
return "TT_HASH";
|
||||
case TT_ASTERISK:
|
||||
return "TT_ASTERISK";
|
||||
case TT_UNDERSCORE:
|
||||
return "TT_UNDERSCORE";
|
||||
case TT_DASH:
|
||||
return "TT_DASH";
|
||||
case TT_PERIOD:
|
||||
return "TT_PERIOD";
|
||||
case TT_BACKTICK:
|
||||
return "TT_BACKTICK";
|
||||
case TT_WHITESPACE:
|
||||
return "TT_WHITESPACE";
|
||||
case TT_NEWLINE:
|
||||
return "TT_NEWLINE";
|
||||
case TT_WORD:
|
||||
return "TT_WORD";
|
||||
case TT_NUMBER:
|
||||
return "TT_NUMBER";
|
||||
}
|
||||
|
||||
return "\0";
|
||||
}
|
||||
|
||||
char*
|
||||
printableOnly(char* input)
|
||||
{
|
||||
char *str = malloc(sizeof(char) * ((strlen(input)*4)+1));
|
||||
int i, j;
|
||||
int len = strlen(input);
|
||||
for(i = 0, j = 0; i < len; i++, j++)
|
||||
{
|
||||
if(input[i] < 0x20 || input[i] > 0x7F)
|
||||
{
|
||||
// hex notation
|
||||
snprintf(&str[j], 5, "\\x%02X", input[i]);
|
||||
j+=3;
|
||||
}
|
||||
else
|
||||
{
|
||||
str[j] = input[i];
|
||||
}
|
||||
}
|
||||
|
||||
str[j] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
#ifndef TOKEN_H
|
||||
#define TOKEN_H
|
||||
|
||||
typedef enum {
|
||||
TT_ILLEGAL,
|
||||
TT_EOF,
|
||||
TT_HASH, // #
|
||||
TT_ASTERISK,
|
||||
TT_UNDERSCORE,
|
||||
TT_DASH,
|
||||
TT_PERIOD,
|
||||
TT_BACKTICK,
|
||||
TT_WHITESPACE,
|
||||
TT_NEWLINE,
|
||||
TT_WORD,
|
||||
TT_NUMBER,
|
||||
} TokenType;
|
||||
|
||||
typedef struct Token {
|
||||
TokenType type;
|
||||
char* literal;
|
||||
int line;
|
||||
int column;
|
||||
char* printBuff;
|
||||
} Token;
|
||||
|
||||
typedef struct TokenList {
|
||||
Token* token;
|
||||
struct TokenList* next;
|
||||
} TokenList;
|
||||
|
||||
TokenList* TokenListAdd(TokenList* current, Token* next);
|
||||
|
||||
char* TokenString(Token* t);
|
||||
char* TokenTypeString(TokenType tt);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue