mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Wrote a basic lexer
This commit is contained in:
34
makefile
Normal file
34
makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
export OUTDIR = out
|
||||
|
||||
all: $(OUTDIR)
|
||||
$(MAKE) -C source
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir $(OUTDIR)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
ifeq ($(findstring CYGWIN, $(shell uname)),CYGWIN)
|
||||
find . -type f -name '*.o' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.a' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.so' -exec rm -f -r -v {} \;
|
||||
find . -empty -type d -delete
|
||||
else ifeq ($(shell uname), Linux)
|
||||
find . -type f -name '*.o' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.a' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.exe' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.dll' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.lib' -exec rm -f -r -v {} \;
|
||||
find . -type f -name '*.so' -exec rm -f -r -v {} \;
|
||||
find . -empty -type d -delete
|
||||
else ifeq ($(OS),Windows_NT)
|
||||
$(RM) *.o *.a *.exe
|
||||
else
|
||||
@echo "Deletion failed - what platform is this?"
|
||||
endif
|
||||
|
||||
rebuild: clean all
|
||||
1
scripts/test.toy
Normal file
1
scripts/test.toy
Normal file
@@ -0,0 +1 @@
|
||||
print "Hello world";
|
||||
11
source/common.h
Normal file
11
source/common.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TOY_VERSION_MAJOR 0
|
||||
#define TOY_VERSION_MINOR 6
|
||||
#define TOY_VERSION_PATCH 0
|
||||
#define TOY_VERSION_BUILD __DATE__
|
||||
|
||||
103
source/debug.c
Normal file
103
source/debug.c
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "debug.h"
|
||||
|
||||
#include "keyword_types.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void printToken(Token* token) {
|
||||
if (token->type == TOKEN_ERROR) {
|
||||
printf("Error\t%d\t%.*s\n", token->line, token->length, token->lexeme);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\t%d\t%d\t", token->type, token->line);
|
||||
|
||||
if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_LITERAL_INTEGER || token->type == TOKEN_LITERAL_FLOAT || token->type == TOKEN_LITERAL_STRING) {
|
||||
printf("%.*s\t", token->length, token->lexeme);
|
||||
} else {
|
||||
char* keyword = findKeywordByType(token->type);
|
||||
|
||||
if (keyword != NULL) {
|
||||
printf("%s", keyword);
|
||||
} else {
|
||||
printf("-");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
//declare the singleton
|
||||
Command command;
|
||||
|
||||
void initCommand(int argc, const char* argv[]) {
|
||||
//default values
|
||||
command.error = false;
|
||||
command.help = false;
|
||||
command.version = false;
|
||||
command.filename = NULL;
|
||||
command.source = NULL;
|
||||
command.verbose = false;
|
||||
|
||||
for (int i = 1; i < argc; i++) { //start at 1 to skip the program name
|
||||
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||
command.help = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
||||
command.version = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")) && i + 1 < argc) {
|
||||
command.filename = (char*)argv[i + 1];
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) && i + 1 < argc) {
|
||||
command.source = (char*)argv[i + 1];
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
|
||||
command.verbose = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
command.error = true;
|
||||
}
|
||||
|
||||
//no arguments
|
||||
if (argc == 1) {
|
||||
command.error = true;
|
||||
}
|
||||
}
|
||||
|
||||
void usageCommand(int argc, const char* argv[]) {
|
||||
printf("Usage: %s [-h | -v | [-d][-f filename | -i source]]\n\n", argv[0]);
|
||||
}
|
||||
|
||||
void helpCommand(int argc, const char* argv[]) {
|
||||
usageCommand(argc, argv);
|
||||
|
||||
printf("-h | --help\t\tShow this help then exit.\n");
|
||||
printf("-v | --version\t\tShow version and copyright information then exit.\n");
|
||||
printf("-f | --file filename\tParse and execute the source file.\n");
|
||||
printf("-i | --input source\tParse and execute this given string of source code.\n");
|
||||
printf("-d | --debug\t\tBe verbose when operating.\n");
|
||||
}
|
||||
|
||||
void copyrightCommand(int argc, const char* argv[]) {
|
||||
printf("Toy Programming Language Interpreter Version %d.%d.%d (built on %s)\n\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, TOY_VERSION_BUILD);
|
||||
printf("Copyright (c) 2020-2022 Kayne Ruse, KR Game Studios\n\n");
|
||||
printf("This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.\n\n");
|
||||
printf("Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:\n\n");
|
||||
printf("1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n\n");
|
||||
printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n");
|
||||
printf("3. This notice may not be removed or altered from any source distribution.\n\n");
|
||||
}
|
||||
24
source/debug.h
Normal file
24
source/debug.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "lexer.h"
|
||||
|
||||
void printToken(Token* token);
|
||||
|
||||
//for processing the command line arguments
|
||||
typedef struct {
|
||||
bool error;
|
||||
bool help;
|
||||
bool version;
|
||||
char* filename;
|
||||
char* source;
|
||||
bool verbose;
|
||||
} Command;
|
||||
|
||||
extern Command command;
|
||||
|
||||
void initCommand(int argc, const char* argv[]);
|
||||
|
||||
void usageCommand(int argc, const char* argv[]);
|
||||
void helpCommand(int argc, const char* argv[]);
|
||||
void copyrightCommand(int argc, const char* argv[]);
|
||||
62
source/keyword_types.c
Normal file
62
source/keyword_types.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "keyword_types.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
KeywordType keywordTypes[] = {
|
||||
//type keywords
|
||||
{TOKEN_NULL, "null"},
|
||||
{TOKEN_BOOLEAN, "bool"},
|
||||
{TOKEN_INTEGER, "int"},
|
||||
{TOKEN_FLOAT, "float"},
|
||||
{TOKEN_STRING, "string"},
|
||||
{TOKEN_ARRAY, "array"},
|
||||
{TOKEN_DICTIONARY, "dictionary"},
|
||||
{TOKEN_FUNCTION, "function"},
|
||||
{TOKEN_ANY, "any"},
|
||||
|
||||
//other keywords
|
||||
{TOKEN_AS, "as"},
|
||||
{TOKEN_ASSERT, "assert"},
|
||||
{TOKEN_BREAK, "break"},
|
||||
{TOKEN_CLASS, "class"},
|
||||
{TOKEN_CONST, "const"},
|
||||
{TOKEN_CONTINUE, "continue"},
|
||||
{TOKEN_DO, "do"},
|
||||
{TOKEN_ELSE, "else"},
|
||||
{TOKEN_EXPORT, "export"},
|
||||
{TOKEN_FOR, "for"},
|
||||
{TOKEN_FOREACH, "foreach"},
|
||||
{TOKEN_IF, "if"},
|
||||
{TOKEN_IMPORT, "import"},
|
||||
{TOKEN_IN, "in"},
|
||||
{TOKEN_OF, "of"},
|
||||
{TOKEN_PRINT, "print"},
|
||||
{TOKEN_RETURN, "return"},
|
||||
{TOKEN_USING, "using"},
|
||||
{TOKEN_VAR, "var"},
|
||||
{TOKEN_WHILE, "while"},
|
||||
|
||||
//literal values
|
||||
{TOKEN_LITERAL_TRUE, "true"},
|
||||
{TOKEN_LITERAL_FALSE, "false"},
|
||||
|
||||
//meta tokens
|
||||
{TOKEN_PASS, "pass"},
|
||||
{TOKEN_ERROR, "error"},
|
||||
|
||||
{TOKEN_EOF, NULL},
|
||||
};
|
||||
|
||||
char* findKeywordByType(TokenType type) {
|
||||
if (type == TOKEN_EOF) {
|
||||
return "EOF";
|
||||
}
|
||||
|
||||
for(int i = 0; keywordTypes[i].keyword; i++) {
|
||||
if (keywordTypes[i].type == type) {
|
||||
return keywordTypes[i].keyword;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
13
source/keyword_types.h
Normal file
13
source/keyword_types.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "token_types.h"
|
||||
|
||||
typedef struct {
|
||||
TokenType type;
|
||||
char* keyword;
|
||||
} KeywordType;
|
||||
|
||||
extern KeywordType keywordTypes[];
|
||||
|
||||
//for debugging
|
||||
char* findKeywordByType(TokenType type);
|
||||
297
source/lexer.c
Normal file
297
source/lexer.c
Normal file
@@ -0,0 +1,297 @@
|
||||
#include "lexer.h"
|
||||
#include "keyword_types.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
//static generic utility functions
|
||||
static void cleanLexer(Lexer* lexer) {
|
||||
lexer->source = NULL;
|
||||
lexer->start = 0;
|
||||
lexer->current = 0;
|
||||
lexer->line = 1;
|
||||
}
|
||||
|
||||
static bool isAtEnd(Lexer* lexer) {
|
||||
return lexer->source[lexer->current] == '\0';
|
||||
}
|
||||
|
||||
static char peek(Lexer* lexer) {
|
||||
return lexer->source[lexer->current];
|
||||
}
|
||||
|
||||
static char peekNext(Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) return '\0';
|
||||
return lexer->source[lexer->current + 1];
|
||||
}
|
||||
|
||||
static char advance(Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) {
|
||||
return '\0';
|
||||
}
|
||||
|
||||
//new line
|
||||
if (lexer->source[lexer->current] == '\n') {
|
||||
lexer->line++;
|
||||
}
|
||||
|
||||
lexer->current++;
|
||||
return lexer->source[lexer->current - 1];
|
||||
}
|
||||
|
||||
static void eatWhitespace(Lexer* lexer) {
|
||||
const char c = peek(lexer);
|
||||
|
||||
switch(c) {
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\t':
|
||||
advance(lexer);
|
||||
break;
|
||||
|
||||
//comments
|
||||
case '/':
|
||||
//eat the line
|
||||
if (peekNext(lexer) == '/') {
|
||||
while (advance(lexer) != '\n' && !isAtEnd(lexer));
|
||||
break;
|
||||
}
|
||||
|
||||
//eat the block
|
||||
if (peekNext(lexer) == '*') {
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
while(!(peek(lexer) == '*' && peekNext(lexer) == '/')) advance(lexer);
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
//tail recursion
|
||||
eatWhitespace(lexer);
|
||||
}
|
||||
|
||||
static bool isDigit(Lexer* lexer) {
|
||||
return peek(lexer) >= '0' && peek(lexer) <= '9';
|
||||
}
|
||||
|
||||
static bool isAlpha(Lexer* lexer) {
|
||||
return
|
||||
(peek(lexer) >= 'A' && peek(lexer) <= 'Z') ||
|
||||
(peek(lexer) >= 'a' && peek(lexer) <= 'z') ||
|
||||
peek(lexer) == '_'
|
||||
;
|
||||
}
|
||||
|
||||
static bool match(Lexer* lexer, char c) {
|
||||
if (peek(lexer) == c) {
|
||||
advance(lexer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//token generators
|
||||
static Token makeErrorToken(Lexer* lexer, char* msg) {
|
||||
Token token;
|
||||
|
||||
token.type = TOKEN_ERROR;
|
||||
token.lexeme = msg;
|
||||
token.length = strlen(msg);
|
||||
token.line = lexer->line;
|
||||
|
||||
if (command.verbose) {
|
||||
printf("err:");
|
||||
printToken(&token);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token makeToken(Lexer* lexer, TokenType type) {
|
||||
Token token;
|
||||
|
||||
token.type = type;
|
||||
token.lexeme = &lexer->source[lexer->current - 1];
|
||||
token.length = 1;
|
||||
token.line = lexer->line;
|
||||
|
||||
if (command.verbose) {
|
||||
printf("tok:");
|
||||
printToken(&token);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token makeIntegerOrFloat(Lexer* lexer) {
|
||||
TokenType type = TOKEN_LITERAL_INTEGER; //what am I making?
|
||||
|
||||
while(isDigit(lexer)) advance(lexer);
|
||||
|
||||
if (peek(lexer) == '.') {
|
||||
type = TOKEN_LITERAL_FLOAT;
|
||||
advance(lexer);
|
||||
while(isDigit(lexer)) advance(lexer);
|
||||
}
|
||||
|
||||
Token token;
|
||||
|
||||
token.type = type;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
if (command.verbose) {
|
||||
if (type == TOKEN_LITERAL_INTEGER) {
|
||||
printf("int:");
|
||||
} else {
|
||||
printf("flt:");
|
||||
}
|
||||
printToken(&token);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token makeString(Lexer* lexer, char terminator) {
|
||||
while (!isAtEnd(lexer) && peek(lexer) != terminator) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
advance(lexer); //eat terminator
|
||||
|
||||
if (isAtEnd(lexer)) {
|
||||
return makeErrorToken(lexer, "Unterminated string");
|
||||
}
|
||||
|
||||
Token token;
|
||||
|
||||
token.type = TOKEN_LITERAL_STRING;
|
||||
token.lexeme = &lexer->source[lexer->start + 1];
|
||||
token.length = lexer->current - lexer->start - 2;
|
||||
token.line = lexer->line;
|
||||
|
||||
if (command.verbose) {
|
||||
printf("str:");
|
||||
printToken(&token);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token makeKeywordOrIdentifier(Lexer* lexer) {
|
||||
advance(lexer); //first letter can only be alpha
|
||||
|
||||
while(isDigit(lexer) || isAlpha(lexer)) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
//scan for a keyword
|
||||
for (int i = 0; keywordTypes[i].keyword; i++) {
|
||||
if (strlen(keywordTypes[i].keyword) == (long unsigned int)(lexer->current - lexer->start) && !strncmp(keywordTypes[i].keyword, &lexer->source[lexer->start], lexer->current - lexer->start)) {
|
||||
Token token;
|
||||
|
||||
token.type = keywordTypes[i].type;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
if (command.verbose) {
|
||||
printf("kwd:");
|
||||
printToken(&token);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
//return an identifier
|
||||
Token token;
|
||||
|
||||
token.type = TOKEN_IDENTIFIER;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
|
||||
if (command.verbose) {
|
||||
printf("idf:");
|
||||
printToken(&token);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
//exposed functions
|
||||
void initLexer(Lexer* lexer, char* source) {
|
||||
cleanLexer(lexer);
|
||||
|
||||
lexer->source = source;
|
||||
}
|
||||
|
||||
Token scanLexer(Lexer* lexer) {
|
||||
eatWhitespace(lexer);
|
||||
|
||||
lexer->start = lexer->current;
|
||||
|
||||
if (isAtEnd(lexer)) return makeToken(lexer, TOKEN_EOF);
|
||||
|
||||
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
|
||||
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
|
||||
|
||||
char c = advance(lexer);
|
||||
|
||||
switch(c) {
|
||||
case '(': return makeToken(lexer, TOKEN_PAREN_LEFT);
|
||||
case ')': return makeToken(lexer, TOKEN_PAREN_RIGHT);
|
||||
case '{': return makeToken(lexer, TOKEN_BRACE_LEFT);
|
||||
case '}': return makeToken(lexer, TOKEN_BRACE_RIGHT);
|
||||
case '[': return makeToken(lexer, match(lexer, ']') ? TOKEN_ARRAY : TOKEN_BRACKET_LEFT);
|
||||
case ']': return makeToken(lexer, TOKEN_BRACKET_RIGHT);
|
||||
|
||||
case '+': return makeToken(lexer, match(lexer, '=') ? TOKEN_PLUS_ASSIGN : match(lexer, '+') ? TOKEN_PLUS_PLUS: TOKEN_PLUS);
|
||||
case '-': return makeToken(lexer, match(lexer, '=') ? TOKEN_MINUS_ASSIGN : match(lexer, '-') ? TOKEN_MINUS_MINUS: TOKEN_MINUS);
|
||||
case '*': return makeToken(lexer, match(lexer, '=') ? TOKEN_MULTIPLY_ASSIGN : TOKEN_MULTIPLY);
|
||||
case '/': return makeToken(lexer, match(lexer, '=') ? TOKEN_DIVIDE_ASSIGN : TOKEN_DIVIDE);
|
||||
case '%': return makeToken(lexer, match(lexer, '=') ? TOKEN_MODULO_ASSIGN : TOKEN_MODULO);
|
||||
|
||||
case '!': return makeToken(lexer, match(lexer, '=') ? TOKEN_NOT_EQUAL : TOKEN_NOT);
|
||||
case '=': return makeToken(lexer, match(lexer, '=') ? TOKEN_EQUAL : TOKEN_ASSIGN);
|
||||
|
||||
case '<': return makeToken(lexer, match(lexer, '=') ? TOKEN_LESS_EQUAL : TOKEN_LESS);
|
||||
case '>': return makeToken(lexer, match(lexer, '=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER);
|
||||
|
||||
case '&': //TOKEN_AND not used
|
||||
if (advance(lexer) != '&') {
|
||||
return makeErrorToken(lexer, "Unexpected '&'");
|
||||
} else {
|
||||
return makeToken(lexer, TOKEN_AND);
|
||||
}
|
||||
|
||||
case '|': return makeToken(lexer, match(lexer, '|') ? TOKEN_OR : TOKEN_PIPE);
|
||||
|
||||
case ':': return makeToken(lexer, TOKEN_COLON);
|
||||
case ';': return makeToken(lexer, TOKEN_SEMICOLON);
|
||||
case ',': return makeToken(lexer, TOKEN_COMMA);
|
||||
case '.':
|
||||
if (peek(lexer) == '.' && peekNext(lexer) == ',') {
|
||||
return makeToken(lexer, TOKEN_REST);
|
||||
}
|
||||
return makeToken(lexer, TOKEN_DOT);
|
||||
|
||||
case '"':
|
||||
return makeString(lexer, c);
|
||||
//TODO: possibly support interpolated strings
|
||||
|
||||
default:
|
||||
return makeErrorToken(lexer, "Unexpected token");
|
||||
}
|
||||
}
|
||||
24
source/lexer.h
Normal file
24
source/lexer.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "token_types.h"
|
||||
|
||||
//lexers are bound to a string of code, and return a single token every time scan is called
|
||||
typedef struct {
|
||||
char* source;
|
||||
int start; //start of the token
|
||||
int current; //current position of the lexer
|
||||
int line; //track this for error handling
|
||||
} Lexer;
|
||||
|
||||
//tokens are intermediaries between lexers and parsers
|
||||
typedef struct {
|
||||
TokenType type;
|
||||
char* lexeme;
|
||||
int length;
|
||||
int line;
|
||||
} Token;
|
||||
|
||||
void initLexer(Lexer* lexer, char* source);
|
||||
Token scanLexer(Lexer* lexer);
|
||||
|
||||
27
source/makefile
Normal file
27
source/makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
CC=gcc
|
||||
|
||||
IDIR =.
|
||||
CFLAGS=$(addprefix -I,$(IDIR)) -g -Wall -W -pedantic
|
||||
LIBS=
|
||||
|
||||
ODIR=obj
|
||||
SRC = $(wildcard *.c)
|
||||
OBJ = $(addprefix $(ODIR)/,$(SRC:.c=.o))
|
||||
|
||||
OUT = ../$(OUTDIR)/toy
|
||||
|
||||
all: $(OBJ)
|
||||
$(CC) -o $(OUT) $^ $(CFLAGS) $(LIBS)
|
||||
|
||||
$(OBJ): | $(ODIR)
|
||||
|
||||
$(ODIR):
|
||||
mkdir $(ODIR)
|
||||
|
||||
$(ODIR)/%.o: %.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
$(RM) $(ODIR)
|
||||
197
source/repl_main.c
Normal file
197
source/repl_main.c
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "debug.h"
|
||||
|
||||
#include "lexer.h"
|
||||
//-#include "parser.h"
|
||||
//#include "toy.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//read a file and return it as a char array
|
||||
char* readFile(char* path) {
|
||||
FILE* file = fopen(path, "rb");
|
||||
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, "Could not open file \"%s\"\n", path);
|
||||
exit(74);
|
||||
}
|
||||
|
||||
fseek(file, 0L, SEEK_END);
|
||||
size_t fileSize = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
char* buffer = (char*)malloc(fileSize + 1);
|
||||
|
||||
if (buffer == NULL) {
|
||||
fprintf(stderr, "Not enough memory to read \"%s\"\n", path);
|
||||
exit(74);
|
||||
}
|
||||
|
||||
size_t bytesRead = fread(buffer, sizeof(char), fileSize, file);
|
||||
|
||||
if (bytesRead < fileSize) {
|
||||
fprintf(stderr, "Could not read file \"%s\"\n", path);
|
||||
exit(74);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
buffer[bytesRead] = '\0';
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/*
|
||||
//run functions
|
||||
void runString(char* source) {
|
||||
Lexer lexer;
|
||||
Parser parser;
|
||||
Toy toy;
|
||||
|
||||
initLexer(&lexer, source);
|
||||
initParser(&parser, &lexer);
|
||||
initToy(&toy);
|
||||
|
||||
Chunk* chunk = scanParser(&parser);
|
||||
|
||||
if (chunk->count > 1 && command.verbose) {
|
||||
printChunk(chunk, " ");
|
||||
}
|
||||
|
||||
executeChunk(&toy, chunk);
|
||||
|
||||
freeChunk(chunk);
|
||||
|
||||
freeToy(&toy);
|
||||
freeParser(&parser);
|
||||
}
|
||||
|
||||
void runFile(char* fname) {
|
||||
char* source = readFile(fname);
|
||||
|
||||
runString(source);
|
||||
|
||||
free((void*)source);
|
||||
}
|
||||
|
||||
void repl() {
|
||||
const int size = 2048;
|
||||
char input[size];
|
||||
memset(input, 0, size);
|
||||
|
||||
Parser parser;
|
||||
Toy toy;
|
||||
|
||||
initToy(&toy);
|
||||
|
||||
for(;;) {
|
||||
printf(">");
|
||||
fgets(input, size, stdin);
|
||||
|
||||
//setup
|
||||
Lexer lexer;
|
||||
|
||||
initLexer(&lexer, input);
|
||||
initParser(&parser, &lexer);
|
||||
|
||||
//run
|
||||
Chunk* chunk = scanParser(&parser);
|
||||
|
||||
if (chunk->count > 1 && command.verbose) {
|
||||
printChunk(chunk, " ");
|
||||
}
|
||||
|
||||
//clean up the memory
|
||||
if (parser.error) {
|
||||
freeChunk(chunk);
|
||||
freeParser(&parser);
|
||||
continue;
|
||||
}
|
||||
|
||||
executeChunk(&toy, chunk);
|
||||
|
||||
if (toy.panic) {
|
||||
toy.panic = false;
|
||||
freeChunk(chunk);
|
||||
freeParser(&parser);
|
||||
continue;
|
||||
}
|
||||
|
||||
freeChunk(chunk);
|
||||
|
||||
//cleanup
|
||||
freeParser(&parser);
|
||||
}
|
||||
|
||||
freeToy(&toy);
|
||||
}
|
||||
*/
|
||||
|
||||
void debug() {
|
||||
Lexer lexer;
|
||||
Token token;
|
||||
|
||||
char* source = readFile(command.filename);
|
||||
|
||||
initLexer(&lexer, source);
|
||||
|
||||
//run the lexer until the end of the source
|
||||
do {
|
||||
token = scanLexer(&lexer);
|
||||
} while(token.type != TOKEN_EOF);
|
||||
}
|
||||
|
||||
//entry point
|
||||
int main(int argc, const char* argv[]) {
|
||||
initCommand(argc, argv);
|
||||
|
||||
//command specific actions
|
||||
if (command.error) {
|
||||
usageCommand(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command.help) {
|
||||
helpCommand(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command.version) {
|
||||
copyrightCommand(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//print this until the interpreter meets the specification
|
||||
if (command.verbose) {
|
||||
printf("Warning! This interpreter is a work in progress, it does not yet meet the %d.%d.%d specification.\n", TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH);
|
||||
}
|
||||
|
||||
if (command.filename) {
|
||||
debug();
|
||||
// runFile(command.filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command.source) {
|
||||
// runString(command.source);
|
||||
|
||||
// Lexer lexer;
|
||||
// initLexer(&lexer, command.source);
|
||||
|
||||
// //debugging
|
||||
// while(true) {
|
||||
// Token token = scanLexer(&lexer);
|
||||
|
||||
// if (token.type == TOKEN_EOF) {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// repl();
|
||||
|
||||
return 0;
|
||||
}
|
||||
91
source/token_types.h
Normal file
91
source/token_types.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum TokenType {
|
||||
//types
|
||||
TOKEN_NULL,
|
||||
TOKEN_BOOLEAN,
|
||||
TOKEN_INTEGER,
|
||||
TOKEN_FLOAT,
|
||||
TOKEN_STRING,
|
||||
TOKEN_ARRAY,
|
||||
TOKEN_DICTIONARY,
|
||||
TOKEN_FUNCTION,
|
||||
TOKEN_ANY,
|
||||
|
||||
//keywords and reserved words
|
||||
TOKEN_AS,
|
||||
TOKEN_ASSERT,
|
||||
TOKEN_BREAK,
|
||||
TOKEN_CLASS,
|
||||
TOKEN_CONST,
|
||||
TOKEN_CONTINUE,
|
||||
TOKEN_DO,
|
||||
TOKEN_ELSE,
|
||||
TOKEN_EXPORT,
|
||||
TOKEN_FOR,
|
||||
TOKEN_FOREACH,
|
||||
TOKEN_IF,
|
||||
TOKEN_IMPORT,
|
||||
TOKEN_IN,
|
||||
TOKEN_OF,
|
||||
TOKEN_PRINT,
|
||||
TOKEN_RETURN,
|
||||
TOKEN_USING,
|
||||
TOKEN_VAR,
|
||||
TOKEN_WHILE,
|
||||
|
||||
//literal values
|
||||
TOKEN_IDENTIFIER,
|
||||
TOKEN_LITERAL_NULL,
|
||||
TOKEN_LITERAL_TRUE,
|
||||
TOKEN_LITERAL_FALSE,
|
||||
TOKEN_LITERAL_INTEGER,
|
||||
TOKEN_LITERAL_FLOAT,
|
||||
TOKEN_LITERAL_STRING,
|
||||
|
||||
//math operators
|
||||
TOKEN_PLUS,
|
||||
TOKEN_MINUS,
|
||||
TOKEN_MULTIPLY,
|
||||
TOKEN_DIVIDE,
|
||||
TOKEN_MODULO,
|
||||
TOKEN_PLUS_ASSIGN,
|
||||
TOKEN_MINUS_ASSIGN,
|
||||
TOKEN_MULTIPLY_ASSIGN,
|
||||
TOKEN_DIVIDE_ASSIGN,
|
||||
TOKEN_MODULO_ASSIGN,
|
||||
TOKEN_PLUS_PLUS,
|
||||
TOKEN_MINUS_MINUS,
|
||||
|
||||
//logical operators
|
||||
TOKEN_PAREN_LEFT,
|
||||
TOKEN_PAREN_RIGHT,
|
||||
TOKEN_BRACKET_LEFT,
|
||||
TOKEN_BRACKET_RIGHT,
|
||||
TOKEN_BRACE_LEFT,
|
||||
TOKEN_BRACE_RIGHT,
|
||||
TOKEN_NOT,
|
||||
TOKEN_NOT_EQUAL,
|
||||
TOKEN_EQUAL,
|
||||
TOKEN_LESS,
|
||||
TOKEN_GREATER,
|
||||
TOKEN_LESS_EQUAL,
|
||||
TOKEN_GREATER_EQUAL,
|
||||
TOKEN_AND,
|
||||
TOKEN_OR,
|
||||
|
||||
//other operators
|
||||
TOKEN_ASSIGN,
|
||||
TOKEN_COLON,
|
||||
TOKEN_SEMICOLON,
|
||||
TOKEN_COMMA,
|
||||
TOKEN_DOT,
|
||||
TOKEN_PIPE,
|
||||
TOKEN_REST,
|
||||
|
||||
//meta tokens
|
||||
TOKEN_PASS,
|
||||
TOKEN_ERROR,
|
||||
TOKEN_EOF,
|
||||
} TokenType;
|
||||
|
||||
Reference in New Issue
Block a user