#!/bin/sh
ctags -R &
-cc -g -D CERISE_TESTS -Wall -Wextra -Werror --std=c99 -o cerisec-test *.c \
+cc -g -D CERISE_TESTS -Wall -Wextra --std=c99 -o cerisec-test *.c \
&& ./cerisec-test \
&& cc -g -Wall -Wextra -Werror --std=c99 -o cerisec *.c
void lex(Parser* ctx);
void lexprintpos(Parser* p, FILE* file, Tok* tok);
void gettoken(Parser* ctx);
-void toplevel(Parser* p);
+void module(Parser* p);
/* Option Parsing
*****************************************************************************/
/* strings */
['"'] = 3,
- /* double character ops */
+ /* potential double character ops */
['='] = 4, ['.'] = 4,
/* potential multi-character ops */
{ "while", WHILE },
};
-static int keywcmp(const void* a, const void* b) {
+static int keywcmp(const void* a, const void* b)
+{
return strcmp(((KeywordDef*)a)->keyword, ((KeywordDef*)b)->keyword);
}
-static inline char* file_load(char* path) {
+static inline char* file_load(char* path)
+{
int fd = -1, nread = 0, length = 0;
struct stat sb = {0};
char* contents = NULL;
- if (((fd = open(path, O_RDONLY, 0)) >= 0) && (fstat(fd, &sb) >= 0) && (sb.st_size > 0)) {
+ if (((fd = open(path, O_RDONLY, 0)) >= 0) && (fstat(fd, &sb) >= 0) && (sb.st_size > 0))
+ {
contents = calloc(sb.st_size + 1u, 1u);
while (sb.st_size && (nread = read(fd, contents+length, sb.st_size)) > 0)
+ {
length += nread, sb.st_size -= nread;
+ }
+ }
+ if (fd > 0)
+ {
+ close(fd);
}
- if (fd > 0) close(fd);
return contents;
}
-static inline void convert_value(Tok* tok) {
+static inline void convert_value(Tok* tok)
+{
switch (tok->type) {
- case STRING: {
+ case STRING:
+ {
size_t len = strlen(tok->text+1);
char* strtext = malloc(len);
strncpy(strtext, tok->text+1, len);
break;
}
- case INT: {
+ case INT:
+ {
tok->value.integer = strtol(tok->text, NULL, 0);
break;
}
- case IDENT: {
+ case IDENT:
+ {
KeywordDef key = { .keyword = tok->text };
KeywordDef* match = bsearch(
&key, Keywords, NUM_KEYWORDS, sizeof(KeywordDef), keywcmp);
- if (match) {
+ if (match)
+ {
tok->type = match->type;
if (tok->type != IDENT)
+ {
convert_value(tok); /* recurse to ensure correct conversion */
+ }
}
break;
}
- case BOOL: {
+ case BOOL:
+ {
tok->value.integer = (tok->text[0] == 't');
break;
}
default:
+ /* nothing to do here */
break;
}
}
-static inline void readtok(Parser* ctx) {
+static inline void readtok(Parser* ctx)
+{
Tok* tok = &(ctx->tok);
char *beg = ctx->file->fpos;
char *curr = ctx->file->fpos;
exit(1);
}
- if (tok->type) {
+ if (tok->type)
+ {
size_t sz = (curr - beg);
tok->text = malloc(sz+1);
tok->text[sz] = '\0';
ctx->file->fpos = curr;
}
-void lexfile(Parser* ctx, char* path) {
+void lexfile(Parser* ctx, char* path)
+{
LexFile* file = calloc(sizeof(LexFile), 1u);
file->path = strdup(path);
file->fbeg = file->fpos = file_load(path);
ctx->file = file;
}
-void lex(Parser* ctx) {
+void lex(Parser* ctx)
+{
ctx->tok.file = ctx->file->path;
ctx->tok.type = NONE;
- while (ctx->tok.type == NONE) {
- if (!ctx->file) {
+ while (ctx->tok.type == NONE)
+ {
+ if (!ctx->file)
+ {
/* no more files left to process */
ctx->tok.type = END_FILE;
ctx->tok.text = "";
return;
- } else if (!(ctx->file->fpos) || !*(ctx->file->fpos)) {
+ }
+ else if (!(ctx->file->fpos) || !*(ctx->file->fpos))
+ {
/* grab the next file to process */
LexFile* f = ctx->file;
ctx->file = f->next;
f->next = ctx->done;
ctx->done = f;
- } else {
+ }
+ else
+ {
/* parse out a token */
readtok(ctx);
}
}
}
-static LexFile* get_file(Parser* p, char const* path) {
+static LexFile* get_file(Parser* p, char const* path)
+{
LexFile* lf = p->file;
while (lf && strcmp(lf->path, path))
+ {
lf = lf->next;
- if (!lf) {
+ }
+ if (!lf)
+ {
lf = p->done;
while (lf && strcmp(lf->path, path))
+ {
lf = lf->next;
+ }
}
return lf;
}
-void lexprintpos(Parser* p, FILE* file, Tok* tok) {
+void lexprintpos(Parser* p, FILE* file, Tok* tok)
+{
size_t line = 1, col = 1;
char* data = get_file(p, tok->file)->fbeg;
char* end = data + tok->offset;
- for (; *data && data < end; data++) {
- if (*data == '\n') {
+ for (; *data && data < end; data++)
+ {
+ if (*data == '\n')
+ {
line++;
col = 1;
- } else {
+ }
+ else
+ {
col++;
}
}
{ "]", ']' },
{ "{", '{' },
{ "}", '}' },
+ { "!", '!' },
{ "and", AND },
{ "array", ARRAY },
{ "begin", BEGIN },
{ "", END_FILE },
};
-
TEST(Lexer recognizes all required tokens)
{
Parser ctx = {0};
/* Driver Modes
*****************************************************************************/
-static int emit_binary(Parser* ctx, int argc, char **argv)
+static int emit_binary(Parser* ctx, int argc, char **argv)
{
(void)ctx, (void)argc, (void)argv;
return 0;
}
-static int emit_library(Parser* ctx, int argc, char **argv)
+static int emit_library(Parser* ctx, int argc, char **argv)
{
(void)ctx, (void)argc, (void)argv;
return 0;
/* Main Routine and Usage
*****************************************************************************/
-void usage(void)
+void usage(void)
{
fprintf(stderr, "%s\n",
"Usage: sclpl [options...] [-A artifact] [file...]\n"
exit(1);
}
-int main(int argc, char **argv)
+int main(int argc, char **argv)
{
/* Option parsing */
OPTBEGIN {
for (; argc; argc--,argv++)
lexfile(&ctx, *argv);
/* Execute the main compiler process */
- if (0 == strcmp("bin", Artifact))
+ if (0 == strcmp("bin", Artifact))
{
return emit_binary(&ctx, argc, argv);
- }
- else if (0 == strcmp("lib", Artifact))
+ }
+ else if (0 == strcmp("lib", Artifact))
{
return emit_library(&ctx, argc, argv);
- }
- else
+ }
+ else
{
fprintf(stderr, "Unknown artifact type: '%s'\n\n", Artifact);
usage();
#define INCLUDE_DEFS
#include "atf.h"
-int main(int argc, char **argv)
+int main(int argc, char **argv)
{
atf_init(argc, argv);
RUN_EXTERN_TEST_SUITE(Lexer);
+ RUN_EXTERN_TEST_SUITE(Grammar);
return atf_print_results();
}
/* Parsing Routines
*****************************************************************************/
-//static Tok* peek(Parser* p) {
-// if (T_NONE == p->tok.type)
-// lex(p);
-// return &(p->tok);
-//}
-//
-//static void error(Parser* parser, const char* fmt, ...) {
-// Tok* tok = peek(parser);
-// va_list args;
-// va_start(args, fmt);
-// lexprintpos(parser, stderr, tok);
-// fprintf(stderr, " error: ");
-// vfprintf(stderr, fmt, args);
-// fprintf(stderr, "\n");
-// va_end(args);
-// exit(1);
-//}
-//
-//static bool matches(Parser* p, TokType type) {
-// return (peek(p)->type == type);
-//}
-//
-//static bool accept(Parser* p, TokType type) {
-// if (matches(p, type)) {
-// p->tok.type = T_NONE;
-// return true;
-// }
-// return false;
-//}
-//
-//static void expect(Parser* p, TokType type) {
-// if (!accept(p, type))
-// error(p, "Unexpected token");
-//}
-//
-//static Tok* expect_val(Parser* p, TokType type) {
-// static Tok token = {0};
-// /* perform the match */
-// if (matches(p, type)) {
-// token = *(peek(p));
-// p->tok.type = T_NONE;
-// } else {
-// error(p, "Unexpected token");
-// }
-// return &token;
-//}
-//
+static Tok* peek(Parser* p)
+{
+ if (NONE == p->tok.type)
+ lex(p);
+ return &(p->tok);
+}
+
+static void error(Parser* parser, const char* fmt, ...)
+{
+ Tok* tok = peek(parser);
+ va_list args;
+ va_start(args, fmt);
+ lexprintpos(parser, stderr, tok);
+ fprintf(stderr, " error: ");
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ exit(1);
+}
+
+static bool matches(Parser* p, TokType type)
+{
+ return (peek(p)->type == type);
+}
+
+static bool accept(Parser* p, TokType type)
+{
+ if (matches(p, type)) {
+ p->tok.type = NONE;
+ return true;
+ }
+ return false;
+}
+
+static void expect(Parser* p, TokType type)
+{
+ if (!accept(p, type))
+ error(p, "Unexpected token");
+}
+
+static Tok* expect_val(Parser* p, TokType type)
+{
+ static Tok token = {0};
+ /* perform the match */
+ if (matches(p, type)) {
+ token = *(peek(p));
+ p->tok.type = NONE;
+ } else {
+ error(p, "Unexpected token");
+ }
+ return &token;
+}
+
+static char* expect_text(Parser* p, TokType type)
+{
+ return strdup(expect_val(p, type)->text);
+}
+
//static int consume(Parser* p) {
// int type = peek(p)->type;
// if (!accept(p, type))
/* Grammar Definition
*****************************************************************************/
-void toplevel(Parser* p) {
+
+void import_list(Parser* p)
+{
parse_enter();
- (void)p;
+ expect(p, IMPORT);
+ while (1)
+ {
+ expect(p, IDENT);
+ if (accept(p, '='))
+ {
+ expect(p, IDENT);
+ }
+ if (matches(p, ';'))
+ {
+ break;
+ }
+ expect(p, ',');
+ }
+ expect(p, ';');
parse_exit();
}
+
+void module(Parser* p)
+{
+ parse_enter();
+ expect(p, MODULE);
+ char* sname = expect_text(p, IDENT);
+ /* TODO: Check that it matches filename here */
+ expect(p, ';');
+ if (matches(p, IMPORT))
+ {
+ import_list(p);
+ }
+// declaration_seq(p);
+ if (accept(p, BEGIN))
+ {
+// statement_seq(p);
+ }
+ expect(p, END);
+ char* ename = expect_text(p, IDENT);
+ if (strcmp(sname, ename))
+ {
+ error(p, "Expected module name '%s', recieved '%s' instead", sname, ename);
+ }
+ expect(p, ';');
+ parse_exit();
+}
+
+/* Grammar Unit Tests
+ *****************************************************************************/
+#ifdef CERISE_TESTS
+#include "atf.h"
+
+Parser Ctx = {0};
+
+void parse_module(char* fname, char* string)
+{
+ memset(&Ctx, 0, sizeof(Ctx));
+ LexFile* file = calloc(sizeof(LexFile), 1u);
+ file->path = strdup(fname);
+ file->fbeg = file->fpos = strdup(string);
+ file->next = Ctx.file;
+ Ctx.file = file;
+ module(&Ctx);
+}
+
+TEST_SUITE(Grammar)
+{
+ TEST(Should parse basic module syntax)
+ {
+ parse_module("Empty",
+ "module Empty; end Empty;");
+ parse_module("ModA",
+ "module ModA; import ModB; end ModA;");
+ parse_module("ModA",
+ "module ModA; import ModB, ModC; end ModA;");
+ parse_module("ModA",
+ "module ModA; import B = ModB, C = ModC; end ModA;");
+ }
+}
+#endif