WIP: c emitter, uxn emitter, parser, compiler, examples, cleanup

This commit is contained in:
zongor 2026-04-07 00:13:31 -07:00
parent 8fd281e463
commit b332ecf06d
31 changed files with 1533 additions and 158 deletions

53
arch/linux/main.c Normal file
View File

@ -0,0 +1,53 @@
#include "../../parser.h"
#include <stdio.h>
#include <stdlib.h>
#define EMBED_FILE(name) \
void emit_##name(const char *filename) \
{ \
FILE *f = fopen(filename, "wb"); \
if(f) { \
fwrite(name, 1, name##_len, f); \
fclose(f); \
} \
}
static char* readFile(const 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);
}
buffer[bytesRead] = '\0';
fclose(file);
return buffer;
}
int
main(int argc, char **argv)
{
if(argc != 2) return EXIT_FAILURE;
char* source = readFile(argv[1]);
compile(source);
free(source);
return EXIT_SUCCESS;
}

View File

@ -1 +0,0 @@
// wip

55
build
View File

@ -2,16 +2,32 @@
set -e
if [ -z $ARCH ]; then
ARCH='linux'
fi
if [ -z $MODE ]; then
MODE='debug'
fi
if [ -z $CC ]; then
CC='gcc'
if [ -z $EMIT ]; then
EMIT='c'
fi
case $ARCH in
"linux")
if [ -z $CC ]; then
CC='gcc'
fi
;;
"web")
CC=emcc
;;
esac
# setup dirs
BUILD_DIR=./out
SRC_DIR=./arch/$ARCH
BUILD_DIR=./out/$EMIT/$ARCH
GEN_DIR=$BUILD_DIR/gen
TOOL_SRC=tools/file2header.c
TOOL_EXE=$BUILD_DIR/file2header
@ -47,20 +63,6 @@ if [ ! -f $TOOL_EXE ] || [ $TOOL_SRC -nt $TOOL_EXE ]; then
$CC $TOOL_SRC -o $TOOL_EXE
fi
if [ -d "arch" ]; then
for file in arch/*.c; do
name=$(basename "$file" .c)
out_header="$GEN_DIR/${name}.h"
if [ ! -f "$out_header" ] || [ "$file" -nt "$out_header" ]; then
echo "Packing $file -> $out_header"
$TOOL_EXE "$file" > "$out_header"
fi
done
else
echo "Warning: 'arch' directory not found."
fi
# setup the build flags based on the build mode
case $MODE in
"debug")
@ -71,7 +73,24 @@ case $MODE in
;;
esac
BUILD_CMD="$CC -o $BUILD_DIR/undar main.c -I$GEN_DIR $BUILD_FLAGS"
# build the core VM
VM_BUILD_FLAGS="$BUILD_FLAGS -std=c89 -ffreestanding -nostdlib -fno-builtin"
${CC} -c libc.c -o $BUILD_DIR/libc.o $VM_BUILD_FLAGS
${CC} -c list.c -o $BUILD_DIR/list.o $VM_BUILD_FLAGS
${CC} -c lexer.c -o $BUILD_DIR/lexer.o $VM_BUILD_FLAGS
${CC} -c parser.c -o $BUILD_DIR/parser.o $VM_BUILD_FLAGS
#BUILD_CMD="$CC -o $BUILD_DIR/undar $SRC_DIR/main.c libc.c list.c lexer.c parser.c -I$GEN_DIR $BUILD_FLAGS"
# Set up the final build command
case $ARCH in
"linux")
BUILD_CMD="$CC -o $BUILD_DIR/undar $SRC_DIR/main.c emit/$EMIT/emit.c $LINK_FLAGS $BUILD_DIR/libc.o $BUILD_DIR/list.o $BUILD_DIR/lexer.o $BUILD_DIR/parser.o $BUILD_FLAGS $LINK_FLAGS"
;;
"web")
BUILD_CMD="$CC $SRC_DIR/main.c emit/$EMIT/emit.c $BUILD_DIR/libc.o $BUILD_DIR/list.o $BUILD_DIR/lexer.o $BUILD_DIR/parser.o -o $BUILD_DIR/undar.html $BUILD_FLAGS $LINK_FLAGS"
;;
esac
echo "$BUILD_CMD"
${BUILD_CMD}

76
emit.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef UNDAR_EMIT_H
#define UNDAR_EMIT_H
#include "libc.h"
typedef enum expression_config_e {
PREFIX,
INFIX,
POSTFIX
} ExpressionConfig;
typedef enum output_config_e {
BINARY,
TEXT
} OutputConfig;
ExpressionConfig expression_config();
OutputConfig output_config();
/**
* Prolog is for all of the "setup" boilerplate
*/
void prolog();
/**
* Prolog is for all of the "cleanup" boilerplate
*/
void epilogue();
/**
* Emitters
*/
void emit_add();
void emit_sub();
void emit_mul();
void emit_div();
void emit_lt();
void emit_le();
void emit_gt();
void emit_ge();
void emit_ne();
void emit_eq();
void emit_false();
void emit_true();
void emit_nil();
void emit_void();
void emit_int(const char *str, i32 length);
void emit_nat(const char *str, i32 length);
void emit_real(const char *str, i32 length);
void emit_str(const char *str, i32 length);
void emit_bool_type();
void emit_byte_type();
void emit_int_type();
void emit_nat_type();
void emit_real_type();
void emit_str_type();
void emit_u8_type();
void emit_i8_type();
void emit_i16_type();
void emit_u16_type();
void emit_i32_type();
void emit_u32_type();
void emit_f32_type();
void emit_array();
void emit_function();
void emit_plex();
void emit_method();
void emit_trait();
void emit_const();
void emit_print();
void emit_neg();
void emit_not();
void emit_open_paren();
void emit_close_paren();
#endif

258
emit/c/emit.c Normal file
View File

@ -0,0 +1,258 @@
#include "../../emit.h"
#include <stdio.h>
ExpressionConfig
expression_config()
{
return INFIX;
}
OutputConfig
output_config()
{
return TEXT;
}
void
prolog()
{
}
void
epilogue()
{
}
void
emit_add()
{
printf("+ ");
}
void
emit_sub()
{
printf("- ");
}
void
emit_mul()
{
printf("* ");
}
void
emit_div()
{
printf("/ ");
}
void
emit_lt()
{
printf("< ");
}
void
emit_le()
{
printf("<= ");
}
void
emit_gt()
{
printf("> ");
}
void
emit_ge()
{
printf(">= ");
}
void
emit_ne()
{
printf("!= ");
}
void
emit_eq()
{
printf("== ");
}
void
emit_false()
{
printf("false ");
}
void
emit_true()
{
printf("true ");
}
void
emit_nil()
{
printf("NULL ");
}
void
emit_neg()
{
printf("-");
}
void
emit_not()
{
printf("!");
}
void
emit_void()
{
}
void
emit_bool_type()
{
}
void
emit_byte_type()
{
}
void
emit_int_type()
{
}
void
emit_nat_type()
{
}
void
emit_real_type()
{
}
void
emit_str_type()
{
}
void
emit_u8_type()
{
}
void
emit_i8_type()
{
}
void
emit_i16_type()
{
}
void
emit_u16_type()
{
}
void
emit_i32_type()
{
}
void
emit_u32_type()
{
}
void
emit_f32_type()
{
}
void
emit_int(const char *str, i32 length)
{
printf("%.*s ", length, str);
}
void
emit_nat(const char *str, i32 length)
{
printf("%.*s ", length, str);
}
void
emit_real(const char *str, i32 length)
{
printf("%.*s ", length, str);
}
void
emit_str(const char *str, i32 length)
{
printf("%.*s ", length, str);
}
void
emit_array()
{
}
void
emit_function()
{
}
void
emit_plex()
{
}
void
emit_method()
{
}
void
emit_trait()
{
}
void
emit_const()
{
}
void
emit_print()
{
printf("printf(\"%%s\", ");
}
void
emit_open_paren()
{
printf("(");
}
void
emit_close_paren()
{
printf(")");
}

View File

257
emit/uxntal/emit.c Normal file
View File

@ -0,0 +1,257 @@
#include "../../emit.h"
#include <stdio.h>
ExpressionConfig
expression_config()
{
return POSTFIX;
}
OutputConfig
output_config()
{
return TEXT;
}
void
prolog()
{
}
void
epilogue()
{
}
void
emit_add()
{
printf("+ ");
}
void
emit_sub()
{
printf("- ");
}
void
emit_mul()
{
printf("* ");
}
void
emit_div()
{
printf("/ ");
}
void
emit_lt()
{
printf("< ");
}
void
emit_le()
{
printf("<= ");
}
void
emit_gt()
{
printf("> ");
}
void
emit_ge()
{
printf(">= ");
}
void
emit_ne()
{
printf("!= ");
}
void
emit_eq()
{
printf("== ");
}
void
emit_false()
{
printf("false ");
}
void
emit_true()
{
printf("true ");
}
void
emit_nil()
{
printf("NULL ");
}
void
emit_neg()
{
printf("-");
}
void
emit_not()
{
printf("!");
}
void
emit_void()
{
}
void
emit_bool_type()
{
}
void
emit_byte_type()
{
}
void
emit_int_type()
{
}
void
emit_nat_type()
{
}
void
emit_real_type()
{
}
void
emit_str_type()
{
}
void
emit_u8_type()
{
}
void
emit_i8_type()
{
}
void
emit_i16_type()
{
}
void
emit_u16_type()
{
}
void
emit_i32_type()
{
}
void
emit_u32_type()
{
}
void
emit_f32_type()
{
}
void
emit_int(const char *str, i32 length)
{
printf("%.*s ", length, str);
}
void
emit_nat(const char *str, i32 length)
{
printf("%.*s ", length, str);
}
void
emit_real(const char *str, i32 length)
{
printf("%.*s ", length, str);
}
void
emit_str(const char *str, i32 length)
{
printf("%.*s ", length, str);
}
void
emit_array()
{
}
void
emit_function()
{
}
void
emit_plex()
{
}
void
emit_method()
{
}
void
emit_trait()
{
}
void
emit_const()
{
}
void
emit_print()
{
printf("printf(\"%%s\", ");
}
void
emit_open_paren()
{
printf("(");
}
void
emit_close_paren()
{
printf(")");
}

23
lexer.c
View File

@ -1,5 +1,3 @@
#include <string.h>
#include "lexer.h"
typedef struct lexer_s Lexer;
@ -83,7 +81,7 @@ error_token(const char *message)
Token token;
token.type = TOKEN_ERROR;
token.start = message;
token.length = (i32)strlen(message);
token.length = (i32)slen(message);
token.line = lexer.line;
return token;
}
@ -105,11 +103,11 @@ skip_whitespace()
break;
case '/':
if(peek_next() == '/') {
// Single-line comment: skip until newline or end of file
/* Single-line comment: skip until newline or end of file */
advance();
while(peek() != '\n' && !is_at_end()) advance();
} else if(peek_next() == '*') {
// Multi-line comment: skip until '*/' or end of file
/* Multi-line comment: skip until '* /' or end of file */
advance();
advance();
while(!is_at_end()) {
@ -117,12 +115,12 @@ skip_whitespace()
if(peek() == '*' && peek_next() == '/') {
advance();
advance();
break; // Exit loop, comment ended
break; /* Exit loop, comment ended */
}
advance();
}
} else {
return; // Not a comment, let tokenization handle it
return; /* Not a comment, let tokenization handle it */
}
break;
default:
@ -135,7 +133,7 @@ static TokenType
check_keyword(i32 start, i32 length, const char *rest, TokenType type)
{
if(lexer.current - lexer.start == start + length &&
memcmp(lexer.start + start, rest, length) == 0) {
sleq(lexer.start + start, rest, length) == 0) {
return type;
}
@ -232,9 +230,10 @@ identifierType()
switch(lexer.start[1]) {
case 't':
return check_keyword(2, 1, "r", TOKEN_TYPE_PTR);
case 'l':
return check_keyword(2, 2, "ex", TOKEN_KEYWORD_PLEX);
case 'r':
return check_keyword(2, 3, "int", TOKEN_KEYWORD_PRINT);
}
}
break;
@ -376,14 +375,16 @@ string()
Token
next_token()
{
char c;
char next;
skip_whitespace();
lexer.start = lexer.current;
if(is_at_end()) return make_token(TOKEN_EOF);
char c = advance();
c = advance();
if(is_alpha(c)) return identifier();
char next = peek();
next = peek();
if((c == '-' && is_digit(next)) || is_digit(c)) return number();
switch(c) {

View File

@ -20,6 +20,7 @@ typedef enum {
TOKEN_TYPE_REAL,
TOKEN_TYPE_STR,
TOKEN_TYPE_BOOL,
TOKEN_TYPE_BYTE,
TOKEN_TYPE_VOID,
TOKEN_TYPE_PTR,
TOKEN_KEYWORD_PLEX,
@ -46,6 +47,7 @@ typedef enum {
TOKEN_KEYWORD_NIL,
TOKEN_KEYWORD_TRUE,
TOKEN_KEYWORD_FALSE,
TOKEN_KEYWORD_PRINT,
TOKEN_OPERATOR_NOT,
TOKEN_OPERATOR_AND,
TOKEN_OPERATOR_OR,

2
libc.c
View File

@ -76,7 +76,7 @@ snlen(const char *str, u32 max_len)
}
void *
aaloc(Arena *arena, u32 size)
aalloc(Arena *arena, u32 size)
{
u32 pos;
if(arena == nil) return nil;

24
list.c
View File

@ -22,6 +22,7 @@ node_value(Node *n)
void *
list_push(Arena *arena, List *list, void *data, u32 data_size)
{
void *dest;
void *ptr = aalloc(arena, sizeof(Node) + data_size);
Node *node = (Node *)ptr;
@ -38,7 +39,7 @@ list_push(Arena *arena, List *list, void *data, u32 data_size)
list->count++;
void *dest = node_value(node);
dest = node_value(node);
if (data && data_size > 0) {
mcpy(dest, data, data_size);
}
@ -49,9 +50,10 @@ list_push(Arena *arena, List *list, void *data, u32 data_size)
void
list_foreach(List *list, list_iter_fn func)
{
Node *curr;
if (!list || !func) return;
Node *curr = list->head;
curr = list->head;
while (curr) {
func(node_value(curr));
curr = curr->next;
@ -61,11 +63,12 @@ list_foreach(List *list, list_iter_fn func)
void *
list_get(List *list, u32 index)
{
u32 i;
Node *curr;
if (!list || index >= list->count) return nil;
Node *curr = list->head;
// Walk to the desired index
for (u32 i = 0; i < index; i++) {
curr = list->head;
for (i = 0; i < index; i++) {
curr = curr->next;
}
@ -73,7 +76,7 @@ list_get(List *list, u32 index)
}
void
list_set(List *list, u32 index, const void *data, u32 size)
list_set(List *list, u32 index, void *data, u32 size)
{
void *target = list_get(list, index);
if (target) {
@ -82,13 +85,16 @@ list_set(List *list, u32 index, const void *data, u32 size)
}
void *
list_find(List *list, compare_fn compare, const void *target)
list_find(List *list, compare_fn compare, void *target)
{
Node *curr;
void *data;
if (!list || !compare) return nil;
Node *curr = list->head;
curr = list->head;
while (curr) {
void *data = node_value(curr);
data = node_value(curr);
if (compare(data, target)) {
return data;
}

6
list.h
View File

@ -15,15 +15,15 @@ struct list_s {
u32 count;
};
typedef bool (*compare_fn)(const void *data, const void *target);
typedef bool (*compare_fn)(void *data, void *target);
typedef void (*list_iter_fn)(void *data);
List *new_list(Arena *arena);
void *node_value(Node *n);
void *list_push(Arena *arena, List *list, void *data, u32 data_size);
void list_foreach(List *list, list_iter_fn func);
void *list_find(List *list, compare_fn compare, const void *target);
void *list_find(List *list, compare_fn compare, void *target);
void *list_get(List *list, u32 index);
void list_set(List *list, u32 index, const void *data, u32 size);
void list_set(List *list, u32 index, void *data, u32 size);
#endif

26
main.c
View File

@ -1,26 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#define EMBED_FILE(name) \
void emit_##name(const char *filename) \
{ \
FILE *f = fopen(filename, "wb"); \
if(f) { \
fwrite(name, 1, name##_len, f); \
fclose(f); \
} \
}
int
main(int argc, char **argv)
{
char *name;
if(argc > 1)
name = argv[1];
else
name = "'u'";
printf("nuqneH %s?\n", name);
return EXIT_SUCCESS;
}

347
parser.c
View File

@ -1,20 +1,8 @@
#include "parser.h"
#include "emit.h"
Parser parser;
bool
advance()
{
parser.previous = parser.current;
for(;;) {
parser.current = next_token();
if(parser.current.type != TOKEN_ERROR) return true;
return false;
}
}
/****************************************************
* Scope
***************************************************/
@ -30,7 +18,7 @@ scope_push(Arena *arena)
}
void
scope_pop(Arena *arena)
scope_pop()
{
Scope *prev = parser.current_scope->parent;
parser.current_scope = prev;
@ -39,33 +27,33 @@ scope_pop(Arena *arena)
Symbol *
scope_get_symbol(Scope *scope, const char *name, u32 name_length)
{
if (!scope) return nil;
u32 count, i;
if(!scope) return nil;
u32 count = scope->symbols->count;
for (u32 i = 0; i < count; i++) {
count = scope->symbols->count;
for(i = 0; i < count; i++) {
Symbol *sym = list_get(scope->symbols, i);
if (sleq(sym->name, name, name_length)) {
return sym;
}
if(sleq(sym->name, name, name_length)) return sym;
}
return scope_get_symbol(scope->parent, name, name_length);
}
Symbol *
scope_add_symbol(Arena *arena, const char *name, u32 name_length, SymbolType type, u32 size)
scope_add_symbol(Arena *arena, const char *name, u32 name_length,
SymbolType type, u32 size)
{
Symbol *sym = scope_get_symbol(parser.current_scope, name, name_length);
if (sym != nil) return sym;
if(sym != nil) return sym;
sym = aalloc(arena, sizeof(Symbol));
scpy(sym->name, name, slen(name));
sym->name_length = slen(name);
sym->type = type;
sym->secondary_type = VOID; // Default
sym->secondary_type = VOID;
sym->size = size;
sym->ref = 0;
if (type == PLEX || type == METHOD || type == TRAIT) {
if(type == PLEX || type == METHOD || type == TRAIT) {
sym->args = new_list(arena);
sym->fields = new_list(arena);
}
@ -78,74 +66,325 @@ scope_add_symbol(Arena *arena, const char *name, u32 name_length, SymbolType typ
/****************************************************
* Parser
***************************************************/
bool
parse_trait()
advance()
{
return true;
parser.previous = parser.current;
for(;;) {
parser.current = next_token();
if(parser.current.type != TOKEN_ERROR) return true;
return false;
}
}
static bool
check(TokenType type)
{
return parser.current.type == type;
}
bool
parse_plex()
consume(TokenType type)
{
if(check(type)) {
advance();
return true;
}
return false;
}
static bool
match(TokenType type)
{
if(!check(type)) return false;
advance();
return true;
}
bool
parse_function()
static void expression();
static void statement();
static void declaration();
static ParseRule *get_rule(TokenType type);
static void parse_precedence(Precedence precedence);
static void
binary()
{
return true;
TokenType operatorType = parser.previous.type;
ParseRule *rule = get_rule(operatorType);
switch(operatorType) {
case TOKEN_BANG_EQ:
emit_ne();
break;
case TOKEN_EQ_EQ:
emit_eq();
break;
case TOKEN_GT:
emit_ge();
break;
case TOKEN_GTE:
emit_ge();
break;
case TOKEN_LT:
emit_lt();
break;
case TOKEN_LTE:
emit_le();
break;
case TOKEN_PLUS:
emit_add();
break;
case TOKEN_MINUS:
emit_sub();
break;
case TOKEN_STAR:
emit_mul();
break;
case TOKEN_SLASH:
emit_div();
break;
default:
return;
}
/* Move to above switch statement for postfix */
parse_precedence((Precedence)(rule->precedence + 1));
}
bool
parse_if()
static void
literal()
{
return true;
switch(parser.previous.type) {
case TOKEN_KEYWORD_FALSE:
emit_false();
break;
case TOKEN_KEYWORD_TRUE:
emit_true();
break;
case TOKEN_KEYWORD_NIL:
emit_nil();
break;
default:
return;
}
}
bool
parse_loop()
static void
expression()
{
return true;
parse_precedence(PREC_ASSIGNMENT);
}
bool
parse_while()
static void
declaration()
{
return true;
if(match(TOKEN_TYPE_INT)) {
emit_int_type();
} else if(match(TOKEN_TYPE_NAT)) {
emit_nat_type();
} else if(match(TOKEN_TYPE_REAL)) {
emit_real_type();
} else if(match(TOKEN_TYPE_STR)) {
emit_str_type();
} else if(match(TOKEN_TYPE_BOOL)) {
emit_bool_type();
} else if(match(TOKEN_TYPE_BYTE)) {
emit_byte_type();
} else if(match(TOKEN_TYPE_U8)) {
emit_u8_type();
} else if(match(TOKEN_TYPE_I8)) {
emit_i8_type();
} else if(match(TOKEN_TYPE_U16)) {
emit_u16_type();
} else if(match(TOKEN_TYPE_I16)) {
emit_i16_type();
} else if(match(TOKEN_IDENTIFIER)) {
/**
* maybe a Plex?
* lookup plex defs
* if not then
*/
}
statement();
}
bool
parse_for()
static void
statement()
{
return true;
if(match(TOKEN_KEYWORD_PRINT))
emit_print();
else
expression();
}
bool
parse_return()
static void
grouping()
{
return true;
emit_open_paren();
expression();
emit_close_paren();
consume(TOKEN_RPAREN);
}
bool
parse_assignment()
static void
number()
{
return true;
emit_int(parser.previous.start, parser.previous.length);
}
bool
parse_expression()
static void
string()
{
return true;
emit_str(parser.previous.start, parser.previous.length);
}
bool
parse_statement()
static void
unary()
{
return true;
TokenType operatorType = parser.previous.type;
switch(operatorType) {
case TOKEN_MINUS:
emit_neg();
break;
case TOKEN_BANG:
emit_not();
break;
default:
return;
}
/* Move to above switch statement for postfix */
parse_precedence(PREC_UNARY);
}
ParseRule rules[] = {
/* 0 */ {NULL, NULL, PREC_NONE},
/* 1 */ {NULL, NULL, PREC_NONE},
/* 2 */ {NULL, NULL, PREC_NONE},
/* 3 */ {number, NULL, PREC_NONE}, /* TOKEN_LITERAL_INT */
/* 4 */ {number, NULL, PREC_NONE}, /* TOKEN_LITERAL_NAT */
/* 5 */ {number, NULL, PREC_NONE}, /* TOKEN_LITERAL_REAL */
/* 6 */ {string, NULL, PREC_NONE}, /* TOKEN_LITERAL_STR */
/* 7 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_I8 */
/* 8 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_I16 */
/* 9 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_INT */
/*10 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_U8 */
/*11 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_U16 */
/*12 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_NAT */
/*13 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_REAL */
/*14 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_STR */
/*15 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_BOOL */
/*16 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_BYTE */
/*17 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_VOID */
/*18 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_PTR */
/*19 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_PLEX */
/*20 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_FN */
/*21 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_CONST */
/*22 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_IF */
/*23 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_IS */
/*24 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_AS */
/*25 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_ELSE */
/*26 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_WHILE */
/*27 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_FOR */
/*28 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_RETURN */
/*29 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_USE */
/*30 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_INIT */
/*31 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_THIS */
/*32 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_GLOBAL */
/*33 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_OPEN */
/*34 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_READ */
/*35 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_WRITE */
/*36 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_STAT */
/*37 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_CLOSE */
/*38 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_LOOP */
/*39 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_DO */
/*40 */ {literal, NULL, PREC_NONE}, /* TOKEN_KEYWORD_NIL */
/*41 */ {literal, NULL, PREC_NONE}, /* TOKEN_KEYWORD_TRUE */
/*42 */ {literal, NULL, PREC_NONE}, /* TOKEN_KEYWORD_FALSE */
/*43 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_PRINT */
/*44 */ {NULL, NULL, PREC_NONE}, /* TOKEN_OPERATOR_NOT */
/*45 */ {NULL, NULL, PREC_NONE}, /* TOKEN_OPERATOR_AND */
/*46 */ {NULL, NULL, PREC_NONE}, /* TOKEN_OPERATOR_OR */
/*47 */ {unary, NULL, PREC_NONE}, /* TOKEN_BANG */
/*48 */ {NULL, binary, PREC_EQUALITY}, /* TOKEN_BANG_EQ */
/*49 */ {NULL, NULL, PREC_NONE}, /* TOKEN_EQ */
/*50 */ {NULL, binary, PREC_EQUALITY}, /* TOKEN_EQ_EQ */
/*51 */ {NULL, NULL, PREC_NONE}, /* TOKEN_AND */
/*52 */ {NULL, NULL, PREC_NONE}, /* TOKEN_AND_AND */
/*53 */ {NULL, NULL, PREC_NONE}, /* TOKEN_PIPE */
/*54 */ {NULL, NULL, PREC_NONE}, /* TOKEN_PIPE_PIPE */
/*55 */ {NULL, NULL, PREC_NONE}, /* TOKEN_QUESTION */
/*56 */ {NULL, NULL, PREC_NONE}, /* TOKEN_QUESTION_DOT */
/*57 */ {NULL, binary, PREC_TERM}, /* TOKEN_PLUS */
/*58 */ {unary, binary, PREC_TERM}, /* TOKEN_MINUS */
/*59 */ {NULL, binary, PREC_FACTOR}, /* TOKEN_STAR */
/*60 */ {NULL, binary, PREC_FACTOR}, /* TOKEN_SLASH */
/*61 */ {NULL, NULL, PREC_NONE}, /* TOKEN_MESH */
/*62 */ {NULL, NULL, PREC_NONE}, /* TOKEN_BIG_MONEY */
/*63 */ {NULL, binary, PREC_COMPARISON}, /* TOKEN_GT */
/*64 */ {NULL, binary, PREC_COMPARISON}, /* TOKEN_LT */
/*65 */ {NULL, binary, PREC_COMPARISON}, /* TOKEN_GTE */
/*66 */ {NULL, binary, PREC_COMPARISON}, /* TOKEN_LTE */
/*67 */ {NULL, NULL, PREC_NONE}, /* TOKEN_DOT */
/*68 */ {NULL, NULL, PREC_NONE}, /* TOKEN_COMMA */
/*69 */ {NULL, NULL, PREC_NONE}, /* TOKEN_COLON */
/*70 */ {NULL, NULL, PREC_NONE}, /* TOKEN_CARET */
/*71 */ {NULL, NULL, PREC_NONE}, /* TOKEN_SEMICOLON */
/*72 */ {grouping, NULL, PREC_NONE}, /* TOKEN_LPAREN */
/*73 */ {NULL, NULL, PREC_NONE}, /* TOKEN_RPAREN */
/*74 */ {NULL, NULL, PREC_NONE}, /* TOKEN_LBRACE */
/*75 */ {NULL, NULL, PREC_NONE}, /* TOKEN_RBRACE */
/*76 */ {NULL, NULL, PREC_NONE}, /* TOKEN_LBRACKET */
/*77 */ {NULL, NULL, PREC_NONE}, /* TOKEN_RBRACKET */
/*78 */ {NULL, NULL, PREC_NONE} /* TOKEN_ARROW_RIGHT */
};
static void
parse_precedence(Precedence precedence)
{
ParseFn prefixRule;
ParseFn infixRule;
advance();
prefixRule = get_rule(parser.previous.type)->prefix;
if(prefixRule == NULL) {
return;
}
prefixRule();
while(precedence <= get_rule(parser.current.type)->precedence) {
advance();
infixRule = get_rule(parser.previous.type)->infix;
infixRule();
}
}
static ParseRule *
get_rule(TokenType type)
{
return &rules[type];
}
bool
compile(char *source)
{
init_lexer(source);
advance();
prolog();
while(!match(TOKEN_EOF)) declaration();
epilogue();
return true;
}

View File

@ -4,6 +4,7 @@
#include "libc.h"
#include "list.h"
#include "lexer.h"
#include "emit.h"
typedef enum symbol_type_e {
VOID,
@ -25,12 +26,13 @@ typedef enum symbol_type_e {
PLEX,
METHOD,
TRAIT,
CONST,
CONST
} SymbolType;
typedef struct symbol_s Symbol;
typedef struct scope_s Scope;
typedef struct parser_s Parser;
typedef struct parse_rule_s ParseRule;
#define MAX_SYMBOL_NAME_LENGTH 64
struct symbol_s {
@ -49,6 +51,28 @@ struct scope_s {
List *symbols; /* list of symbols that live in this scope */
};
typedef enum {
PREC_NONE,
PREC_ASSIGNMENT, /* = */
PREC_OR, /* or */
PREC_AND, /* and */
PREC_EQUALITY, /* == != */
PREC_COMPARISON, /* < > <= >= */
PREC_TERM, /* + - */
PREC_FACTOR, /* * / */
PREC_UNARY, /* ! - */
PREC_CALL, /* . () */
PREC_PRIMARY
} Precedence;
typedef void (*ParseFn)();
struct parse_rule_s {
ParseFn prefix;
ParseFn infix;
Precedence precedence;
};
struct parser_s {
Scope *current_scope;
Token current;
@ -57,4 +81,7 @@ struct parser_s {
bool compile(char *source);
extern void emit_return();
extern void emit_constant();
#endif

8
test/add.ul Executable file
View File

@ -0,0 +1,8 @@
/**
* Add two numbers together
*/
function add(int a, int b) int {
return a + b;
}
print(add(1, 1) as str);

5
test/bang.ul Executable file
View File

@ -0,0 +1,5 @@
bool flag = false;
if (!flag) {
print("flag is false");
}

1
test/expression.ul Normal file
View File

@ -0,0 +1 @@
!(5 - 4 > 3 * 2 == false)

9
test/fib.ul Executable file
View File

@ -0,0 +1,9 @@
/**
* Recursively calculate fibonacci
*/
function fib(int n) int {
if (n < 2) { return n; }
return fib(n - 2) + fib(n - 1);
}
print(fib(35) as str);

9
test/for.ul Executable file
View File

@ -0,0 +1,9 @@
nat i = 0;
for (nat x in 10) {
print(x as str);
}
for (nat j = 0; j < 10; j = j + 1) {
print(j as str);
}

1
test/hello.ul Executable file
View File

@ -0,0 +1 @@
print("nuqneH 'u'?");

9
test/if.ul Executable file
View File

@ -0,0 +1,9 @@
nat x = 10;
if (x == 10) {
print("x is 10");
} else if (x == 20) {
print("x is 20");
} else {
print("x is something else");
}

9
test/malloc.ul Executable file
View File

@ -0,0 +1,9 @@
/**
* Main function
*/
function main() {
print("Enter a string: ");
str msg = read(32);
print(msg);
return 0;
}

109
test/paint.ul Executable file
View File

@ -0,0 +1,109 @@
/**
* Constants
*/
const byte BLACK = 0;
const byte WHITE = 255;
const byte DARK_GRAY = 73;
const byte GRAY = 146;
const byte LIGHT_GRAY = 182;
byte selected_color = 255;
trait Device {
nat handle;
}
plex Screen implements Device {
nat handle;
nat width;
nat height;
byte[] buffer;
draw() {
write(this, this.buffer, this.buffer.length);
}
}
plex Mouse implements Device {
nat handle;
nat x;
nat y;
bool left;
bool right;
bool middle;
bool btn4;
}
/**
* Main function
*/
function main() {
Screen screen = open("/dev/screen/0", 0);
Mouse mouse = open("/dev/mouse/0", 0);
outline_swatch(screen, BLACK, 1, 1);
outline_swatch(screen, WHITE, 21, 1);
screen.draw();
loop {
mouse.refresh();
if (!mouse.left) continue;
int box_size = 20;
int x = 1;
int y = 1;
byte color = BLACK;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
color = WHITE;
x = 21;
outlined_swatch(screen, color, x, y);
set_color(box_size, x, y, mouse.x, mouse.y, color);
screen.draw();
rectangle(screen, selected_color, x, y, 5, 5);
}
exit(0);
}
/**
* Checks if the click is within the bound and update the selected color if so.
*/
function set_color(int box_size, int bx, int by, int mx, int my, byte color) {
int right = bx + box_size;
int bottom = by + box_size;
if (mx < bx) return;
if (mx > right) return;
if (my < by) return;
if (my > bottom) return;
selected_color = color;
}
/**
* Draw a color box with a grey outline, if selected use a darker color
*/
function outline_swatch(Device screen, byte color, int x, int y) {
byte bg_color = GRAY;
if (selected_color == color) {
bg_color = DARK_GRAY;
}
rectangle(screen, bg_color, x, y, 20, 20);
rectangle(screen, color, x + 2, y + 2, 17, 17);
}
/**
* Draw a rectangle
*/
function rectangle(Device screen, byte color, int x, int y, int width, int height) {
int base = y * screen.width + x + screen.buffer.ptr + 4;
for (int i = height; i > 0; i--) {
int row = base + width;
memset(screen.buffer, row, color, width);
base += screen.width;
}
}

152
test/plex.c Normal file
View File

@ -0,0 +1,152 @@
#include <stdio.h>
#include <string.h>
typedef struct arena_s Arena;
struct arena_s {
unsigned char *tape;
unsigned int count;
unsigned int capacity;
};
void *aalloc(Arena *arena, unsigned int size){
unsigned int pos;
if(arena == NULL) return NULL;
if(arena->count + size > arena->capacity) return NULL;
pos = arena->count;
arena->count += size;
return (void *)&arena->tape[pos];
}
unsigned int
scpy(char *to, const char *from, unsigned int length)
{
unsigned int i;
if(to == NULL || from == NULL) return -1;
if(length == 0) return 0;
for(i = 0; i < length - 1 && from[i] != '\0'; i++) to[i] = from[i];
to[i] = '\0';
return 0;
}
char *
ascpy(Arena *arena, const char *start, unsigned int length)
{
char *str;
if(!start) return NULL;
str = (char *)aalloc(arena, length + 1);
if(!str) return NULL;
scpy(str, start, length);
return str;
}
void *
areturn(Arena *arena, unsigned int checkpoint, const void *src, unsigned int size)
{
void *dest;
if(arena == NULL || src == NULL) return NULL;
dest = (void *)&arena->tape[checkpoint];
if (src == dest) return dest;
memmove(dest, src, size);
arena->count = checkpoint + size;
return dest;
}
#define ARENA_RETURN(arena, ckpt, src_ptr, type) \
return (type *)areturn((arena), (ckpt), (src_ptr), sizeof(type))
#define ARENA_RETURN_ARRAY(arena, ckpt, src_ptr, type, count) \
return (type *)areturn((arena), (ckpt), (src_ptr), sizeof(type) * (count))
typedef struct Point Point;
struct Point {
unsigned int x;
unsigned int y;
};
Point *
Point_new(Arena *a, unsigned int x, unsigned int y)
{
Point *this = (Point *)aalloc(a, sizeof(Point));
this->x = x;
this->y = y;
return this;
}
char*
Point_toS(Arena *a, Point *this)
{
unsigned int ckpt = a->count;
char *s = (char*)&a->tape[a->count];
int len = sprintf(s, "[x:%d, y:%d]",
this->x,
this->y);
s[len] = '\0';
a->count += len + 1;
ARENA_RETURN_ARRAY(a, ckpt, s, char, len + 1);
}
typedef struct Rect Rect;
struct Rect {
Point top_left;
Point bottom_right;
unsigned int width;
unsigned int height;
};
Rect *
Rect_new(Arena *a, Point *tl, Point *br, unsigned int width, unsigned int height)
{
Rect *this = (Rect *)aalloc(a, sizeof(Rect));
memcpy(&this->top_left, tl, sizeof(Point));
memcpy(&this->bottom_right, br, sizeof(Point));
this->width = width;
this->height = height;
return this;
}
char*
Rect_toS(Arena *a, Rect *this)
{
unsigned int ckpt = a->count;
char *s1 = Point_toS(a, &this->top_left);
char *s2 = Point_toS(a, &this->bottom_right);
char *s = (char*)&a->tape[a->count];
int len = sprintf(s, "[top_left: %s, bottom_right: %s, width:%d, height:%d]",
s1,
s2,
this->width,
this->height);
s[len] = '\0';
a->count += len + 1;
ARENA_RETURN_ARRAY(a, ckpt, s, char, len + 1);
}
Rect *
create_geometry(Arena *a)
{
unsigned int ckpt = a->count;
Rect *final_rect;
Point *temp_tl;
Point *temp_br;
temp_tl = Point_new(a, 10, 20);
temp_br = Point_new(a, 100, 200);
final_rect = Rect_new(a, temp_tl, temp_br, 90, 180);
ARENA_RETURN(a, ckpt, final_rect, Rect);
}
int main() {
unsigned char tape[64000];
Arena a = {tape, 0, 64000};
Rect *r = create_geometry(&a);
printf("%s\n", Rect_toS(&a, r));
return 0;
}

52
test/plex.ul Executable file
View File

@ -0,0 +1,52 @@
plex Point {
nat x;
nat y;
init(nat x, nat y) {
this.x = x;
this.y = y;
}
toS() {
pln("[x:%d, y:%d]",
this.x,
this.y);
}
}
plex Rect {
Point top_left;
Point bottom_right;
nat width;
nat height;
init(Point tl, Point br, nat width, nat height) {
this.top_left = tl;
this.bottom_right = br;
this.width = width;
this.height = height;
}
toS() {
pln("[top_left: [x:%d, y:%d], bottom_right: [x:%d, y:%d], width:%d, height:%d]]",
this.top_left.x,
this.top_left.y,
this.bottom_right.x,
this.bottom_right.y,
this.width,
this.height);
}
}
function create_geometry() Rect {
Point tl(10, 20);
Point br(100, 50);
Rect final_rect(tl, br, 90, 180);
return final_rect;
}
function main() int {
Rect r = create_geometry();
print(r.toS());
return 0;
}

1
test/simple.ul Executable file
View File

@ -0,0 +1 @@
print((1.0 + 1.0) as str);

21
test/trait.ul Executable file
View File

@ -0,0 +1,21 @@
trait Device {
nat handle;
}
trait Printable {
print();
}
plex Screen implements Device {
nat handle;
nat width;
}
plex Monitor implements Device, Printable {
nat handle;
nat width;
nat height;
print() {
}
}

6
test/while.ul Executable file
View File

@ -0,0 +1,6 @@
nat i = 0;
while (i < 10) {
print(i as str);
i = i + 1;
}

72
test/window.ul Executable file
View File

@ -0,0 +1,72 @@
/**
* Constants
*/
const str screen_namespace = "/dev/screen/0";
const str mouse_namespace = "/dev/mouse/0";
const str terminal_namespace = "/dev/term/0";
const str new_line = "\n";
const byte WHITE = 255;
/**
* Devices
*/
plex Terminal {
nat handle;
}
plex Screen {
nat handle;
nat width;
nat height;
byte[] buffer;
draw() {
write(this, this.buffer, this.buffer_size);
}
}
plex Mouse {
nat handle;
nat x;
nat y;
bool left;
bool right;
bool middle;
bool btn4;
nat size;
}
/**
* Main function
*/
function main() {
Screen screen = open(screen_namespace, 0);
print(screen.handle as str);
print(screen.width as str);
print(screen.size as str);
unsafe {
print(screen.buffer.ptr as str);
}
Mouse mouse = open(mouse_namespace, 0);
screen.draw();
loop {
if (mouse.left) {
unsafe {
screen.buffer[mouse.y * width + mouse.x +
screen.buffer.ptr + 4] = WHITE;
screen.draw();
}
}
}
}
/**
* Print with a newline
*/
function print(str message) {
Terminal term = open(terminal_namespace, 0);
write(term, message, message.length);
write(term, nl, nl.length);
}