Compare commits

..

8 Commits

Author SHA1 Message Date
Mid
b21ce51435 Update examples with QoL parsing additions 2025-10-02 13:47:55 +03:00
Mid
2f67a6f109 Fix dumbing bug 2025-10-02 13:46:08 +03:00
Mid
1177462bda Increase XOPBUFSZ to 64 2025-10-02 13:45:15 +03:00
Mid
246f7a3b71 type_dereference 2025-10-02 13:44:49 +03:00
Mid
2188448b19 Implicitly cast return type 2025-10-02 13:44:35 +03:00
Mid
d46b104d5c Fix operator precedence and introduce auto-deref 2025-10-02 13:44:18 +03:00
Mid
07e3604a8c Set TLC functionType sooner 2025-10-02 13:39:11 +03:00
Mid
fb75434cba Copy cexprs to keep AST nodes unique/unshared 2025-10-02 13:38:27 +03:00
7 changed files with 280 additions and 202 deletions

View File

@ -11,7 +11,7 @@ record ListDLU[T, S; growth] {
}
ListDLU_remove: [T, S; growth]u0(ListDLU[T, S; growth]* this, S index) -> {
T* data0 = &((*((*this).data))[index]);
T* data0 = &(*(*this).data)[index];
T* data1 = data0 + @sizeof T;
S sz = (*this).size;
(*this).size = (*this).size - 1;
@ -33,7 +33,7 @@ ListDLU_add: [T, S; growth]u0(ListDLU[T, S; growth]* this, Alloc *alloc, T value
if((*this).size == (*this).capacity) {
u32 newcap = (*this).capacity + growth;
(*this).capacity = newcap;
(*this).data = ((*alloc).realloc)((*alloc).userdata, (*this).data, newcap * @sizeof T);
(*this).data = (*alloc).realloc((*alloc).userdata, (*this).data, newcap * @sizeof T);
}
(*((*this).data))[(*this).size] = value;

View File

@ -1,5 +1,5 @@
/*
* SOaLDhS: Statically-allocated, Open-addressing, Linear probing, Dynamic hash, sentinels
* SIOaLpDhFt: Statically-allocated, Interleaved KV values, Open-addressing, Linear probing, Dynamic hash, Flag tombstones
*/
record KVPair[K, V] {
@ -7,25 +7,71 @@ record KVPair[K, V] {
V value;
}
record MapSOaLDhS[K, V, S; capacity, sentinel] {
S(K*) hash;
record MapSIOaLpDhFt[K, V, S; capacity] {
S(K*)* hash;
KVPair[capacity] data;
u8[capacity] occupied;
}
MapSOaLDhS_add: [K, V, S; capacity, sentinel]u1(MapSOaLDhS[K, V, S; capacity, sentinel]* this, K* key, V* value) -> {
S index = ((*this).hash)(value);
index = index & (capacity - 1);
MapSIOaLpDhFt_add: [K, V, S; capacity]u1(MapSIOaLpDhFt[K, V, S; capacity]* this, K* key, V* value) -> {
S start = this.hash(key);
start = start & (capacity - 1);
S index = start;
loop {
KVPair[K, V]* pair = &((*this).data[index]);
if(((*pair).key == *key) || ((*pair).value == sentinel)) {
(*pair).key = *key;
(*pair).value = *value;
}
index = index + 1;
}
KVPair[K, V]* pair = &this.data[index];
if(pair.key == *key || this.occupied[index] == 0) {
pair.key = *key;
pair.value = *value;
this.occupied[index] = 1;
return 1;
}
index = (index + 1) & (capacity - 1);
if(index == start) {
return 0;
}
}
};
@instantiate MapSOaLDhS_add[ugpr, ugpr, ugpr; 128, 0];
MapSIOaLpDhFt_get: [K, V, S; capacity]V*(MapSIOaLpDhFt[K, V, S; capacity]* this, K* key) -> {
S start = this.hash(key);
start = start & (capacity - 1);
MapSOaLDhS[ugpr, ugpr, ugpr; 128, 0] map;
S index = start;
loop {
KVPair[K, V]* pair = &this.data[index];
if(pair.key == *key) {
if(this.occupied[index] == 0) {
return 0;
}
return &pair.value;
}
index = (index + 1) & (capacity - 1);
if(index == start) {
return 0;
}
}
};
zero_hash: ugpr(ugpr* val) -> {
return 0;
};
@instantiate MapSIOaLpDhFt_add[ugpr, ugpr, ugpr; 32];
@instantiate MapSIOaLpDhFt_get[ugpr, ugpr, ugpr; 32];
main: u0() -> {
map.hash = &zero_hash;
MapSIOaLpDhFt_get[ugpr, ugpr, ugpr; 32](&map, &test_key);
MapSIOaLpDhFt_add[ugpr, ugpr, ugpr; 32](&map, &test_key, &test_value);
MapSIOaLpDhFt_get[ugpr, ugpr, ugpr; 32](&map, &test_key);
};
loop {}
@section(".data");
MapSIOaLpDhFt[ugpr, ugpr, ugpr; 32] map:;
ugpr test_value: 10;
ugpr test_key: 5;

View File

@ -242,12 +242,19 @@ AST *ast_cast_expr(AST *what, Type *to) {
} else abort();
}
bool copy = false;
// Make sure an unparametrized generic int parameter hasn't sneaked its way in
while(what->nodeKind == AST_EXPR_VAR && what->exprVar.thing->kind == SCOPEITEM_CEXPR && what->exprVar.thing->data.cexpr.concrete) {
what = what->exprVar.thing->data.cexpr.concrete;
copy = true;
}
assert(!(what->nodeKind == AST_EXPR_VAR && what->exprVar.thing->kind == SCOPEITEM_CEXPR));
if(copy) {
what = ast_deep_copy(what);
}
if(type_equal(what->expression.type, to)) return what;
if(!type_is_castable(what->expression.type, to)) {

View File

@ -202,7 +202,6 @@ AST *nct_parse_expression(Parser *P, int lOP) {
P->isInFunction++;
e->chunk = (AST*) nct_parse_chunk(P, 1, 0, scope_new(P->scope), ft);
e->chunk->chunk.functionType = ft;
P->isInFunction--;
@ -287,7 +286,20 @@ AST *nct_parse_expression(Parser *P, int lOP) {
expect(P, TOKEN_PAREN_R);
}
while(maybe(P, TOKEN_DOT)) {
while(maybe(P, TOKEN_DOT) || maybe(P, TOKEN_PAREN_L) || maybe(P, TOKEN_SQUAREN_L)) {
Token op = P->tokens[P->i - 1];
if(op.type == TOKEN_DOT) {
while(type_is_any_pointer(e->expression.type)) {
AST *deref = alloc_node(P, sizeof(ASTExprUnaryOp));
deref->nodeKind = AST_EXPR_UNARY_OP;
deref->exprUnOp.operator = UNOP_DEREF;
deref->exprUnOp.operand = (AST*) e;
deref->exprUnOp.type = type_dereference(e->expression.type);
e = (AST*) deref;
}
assert(e->expression.type->type == TYPE_TYPE_RECORD);
Token fieldTok = expect(P, TOKEN_IDENTIFIER);
@ -312,6 +324,123 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
e = (AST*) d;
} else if(op.type == TOKEN_PAREN_L) {
// Convert a call like foo() to (&foo)()
if(e->expression.type->type == TYPE_TYPE_FUNCTION) {
ASTExprUnaryOp *ref = alloc_node(P, sizeof(*ref));
ref->nodeKind = AST_EXPR_UNARY_OP;
ref->type = type_pointer_wrap(e->expression.type);
ref->operator = UNOP_REF;
ref->operand = e;
e = (AST*) ref;
}
if(e->expression.type->type != TYPE_TYPE_POINTER || e->expression.type->pointer.of->type != TYPE_TYPE_FUNCTION) {
stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Only function types or function pointers may be called.");
}
Type *ftype = e->expression.type->pointer.of;
ASTExprCall *call = alloc_node(P, sizeof(*call));
call->nodeKind = AST_EXPR_CALL;
call->type = ftype->function.ret;
call->what = e;
call->args = NULL;
int argCount = 0;
if(!maybe(P, TOKEN_PAREN_R)) {
while(peek(P, 0).type != TOKEN_PAREN_R && peek(P, 0).type != TOKEN_COMMA) {
call->args = realloc(call->args, (argCount + 1) * sizeof(AST*));
call->args[argCount] = ast_cast_expr(nct_parse_expression(P, 0), ftype->function.args[argCount]);
argCount++;
if(maybe(P, TOKEN_PAREN_R)) {
break;
} else expect(P, TOKEN_COMMA);
}
}
if(argCount != ftype->function.argCount) {
stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Mismatched number of arguments");
}
e = (AST*) call;
} else if(op.type == TOKEN_SQUAREN_L) {
P->scope = scope_new(P->scope);
P->i--;
if(parse_parametrization(P)) {
// Generic type parametrization
// Generic functions are not first-class
assert(e->nodeKind == AST_EXPR_VAR);
assert(e->exprVar.thing != NULL);
assert(e->exprVar.thing->kind == SCOPEITEM_SYMBOL);
char *cname = parametrize_function_name(e->expression.type, e->exprVar.thing->data.symbol.name, P->scope);
ScopeItem *cvte = scope_find(P->scope, cname);
if(!cvte) {
stahp_token(&P->tokens[P->i], "Parametrization %s not found.", cname);
}
e = exprvar(P, cvte);
} else {
// Array access
expect(P, TOKEN_SQUAREN_L);
ASTExprUnaryOp *ref = alloc_node(P, sizeof(*ref));
ref->nodeKind = AST_EXPR_UNARY_OP;
ref->operator = UNOP_REF;
ref->operand = e;
ref->type = type_pointer_wrap(e->expression.type->array.of);
ASTExprBinaryOp *child = alloc_node(P, sizeof(*child));
child->nodeKind = AST_EXPR_BINARY_OP;
child->operands[0] = (AST*) ref;
child->operands[1] = nct_parse_expression(P, 0);
child->operator = BINOP_ADD;
child->type = ref->type;
if(e->expression.type->type != TYPE_TYPE_ARRAY) {
stahp_token(&P->tokens[P->i], "Attempt to index a non-array type");
}
int typesize = type_size(e->expression.type->array.of);
if(typesize != 1) {
ASTExprPrimitive *scale = alloc_node(P, sizeof(*scale));
scale->nodeKind = AST_EXPR_PRIMITIVE;
scale->type = type_u(8 * arch_gpr_size());
scale->val = typesize;
ASTExprBinaryOp *mul = alloc_node(P, sizeof(*mul));
mul->nodeKind = AST_EXPR_BINARY_OP;
mul->type = child->operands[1]->expression.type;
mul->operator = BINOP_MUL;
mul->operands[0] = (AST*) scale;
mul->operands[1] = child->operands[1];
child->operands[1] = (AST*) mul;
}
ASTExprUnaryOp *unop = alloc_node(P, sizeof(*unop));
unop->nodeKind = AST_EXPR_UNARY_OP;
unop->type = e->expression.type->array.of;
unop->operator = UNOP_DEREF;
unop->operand = (AST*) child;
expect(P, TOKEN_SQUAREN_R);
e = (AST*) unop;
}
P->scope = P->scope->parent;
} else abort();
}
return e;
@ -370,128 +499,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
} else return nct_parse_expression(P, lOP + 1);
} else if(lOP == 5) {
AST *ret = nct_parse_expression(P, lOP + 1);
while(peek(P, 0).type == TOKEN_PAREN_L || peek(P, 0).type == TOKEN_SQUAREN_L) {
if(maybe(P, TOKEN_PAREN_L)) {
if(ret->expression.type->type == TYPE_TYPE_FUNCTION) {
ASTExprUnaryOp *ref = alloc_node(P, sizeof(*ref));
ref->nodeKind = AST_EXPR_UNARY_OP;
ref->type = type_pointer_wrap(ret->expression.type);
ref->operator = UNOP_REF;
ref->operand = ret;
ret = (AST*) ref;
}
if(ret->expression.type->type != TYPE_TYPE_POINTER || ret->expression.type->pointer.of->type != TYPE_TYPE_FUNCTION) {
stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Only function types or function pointers may be called.");
}
Type *ftype = ret->expression.type->pointer.of;
ASTExprCall *call = alloc_node(P, sizeof(*call));
call->nodeKind = AST_EXPR_CALL;
call->type = ftype->function.ret;
call->what = ret;
call->args = NULL;
int argCount = 0;
if(!maybe(P, TOKEN_PAREN_R)) {
while(peek(P, 0).type != TOKEN_PAREN_R && peek(P, 0).type != TOKEN_COMMA) {
call->args = realloc(call->args, (argCount + 1) * sizeof(AST*));
call->args[argCount] = ast_cast_expr(nct_parse_expression(P, 0), ftype->function.args[argCount]);
argCount++;
if(maybe(P, TOKEN_PAREN_R)) {
break;
} else expect(P, TOKEN_COMMA);
}
}
if(argCount != ftype->function.argCount) {
stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Mismatched number of arguments");
}
ret = (AST*) call;
} else if(peek(P, 0).type == TOKEN_SQUAREN_L) {
P->scope = scope_new(P->scope);
if(parse_parametrization(P)) {
// Generic type parametrization
// Generic functions are not first-class
assert(ret->nodeKind == AST_EXPR_VAR);
assert(ret->exprVar.thing != NULL);
assert(ret->exprVar.thing->kind == SCOPEITEM_SYMBOL);
char *cname = parametrize_function_name(ret->expression.type, ret->exprVar.thing->data.symbol.name, P->scope);
ScopeItem *cvte = scope_find(P->scope, cname);
if(!cvte) {
stahp_token(&P->tokens[P->i], "Parametrization %s not found.", cname);
}
ret = exprvar(P, cvte);
} else {
// Array access
expect(P, TOKEN_SQUAREN_L);
ASTExprUnaryOp *ref = alloc_node(P, sizeof(*ref));
ref->nodeKind = AST_EXPR_UNARY_OP;
ref->operator = UNOP_REF;
ref->operand = ret;
ref->type = type_pointer_wrap(ret->expression.type->array.of);
ASTExprBinaryOp *child = alloc_node(P, sizeof(*child));
child->nodeKind = AST_EXPR_BINARY_OP;
child->operands[0] = (AST*) ref;
child->operands[1] = nct_parse_expression(P, 0);
child->operator = BINOP_ADD;
child->type = ref->type;
if(ret->expression.type->type != TYPE_TYPE_ARRAY) {
stahp_token(&P->tokens[P->i], "Attempt to index a non-array type");
}
int typesize = type_size(ret->expression.type->array.of);
if(typesize != 1) {
ASTExprPrimitive *scale = alloc_node(P, sizeof(*scale));
scale->nodeKind = AST_EXPR_PRIMITIVE;
scale->type = type_u(8 * arch_gpr_size());
scale->val = typesize;
ASTExprBinaryOp *mul = alloc_node(P, sizeof(*mul));
mul->nodeKind = AST_EXPR_BINARY_OP;
mul->type = child->operands[1]->expression.type;
mul->operator = BINOP_MUL;
mul->operands[0] = (AST*) scale;
mul->operands[1] = child->operands[1];
child->operands[1] = (AST*) mul;
}
ASTExprUnaryOp *unop = alloc_node(P, sizeof(*unop));
unop->nodeKind = AST_EXPR_UNARY_OP;
unop->type = ret->expression.type->array.of;
unop->operator = UNOP_DEREF;
unop->operand = (AST*) child;
expect(P, TOKEN_SQUAREN_R);
ret = (AST*) unop;
}
P->scope = P->scope->parent;
} else abort();
}
return ret;
return nct_parse_expression(P, lOP + 1);
} else if(lOP == 4) {
AST *ret = nct_parse_expression(P, lOP + 1);
@ -583,35 +591,6 @@ AST *nct_parse_expression(Parser *P, int lOP) {
} else if(lOP == 1) {
AST *ret = nct_parse_expression(P, lOP + 1);
if(peek(P, 0).type == TOKEN_DOUBLE_AMPERSAND || peek(P, 0).type == TOKEN_DOUBLE_VERTICAL_BAR) {
while(1) {
BinaryOp op;
if(maybe(P, TOKEN_DOUBLE_AMPERSAND)) op = BINOP_LOGICAL_AND;
else if(maybe(P, TOKEN_DOUBLE_VERTICAL_BAR)) op = BINOP_LOGICAL_OR;
else break;
ASTExprBinaryOp *astop = alloc_node(P, sizeof(*astop));
astop->nodeKind = AST_EXPR_BINARY_OP;
astop->type = NULL;
astop->operator = op;
astop->operands[0] = ret;
ASTExpr *operand = &(astop->operands[1] = nct_parse_expression(P, lOP + 1))->expression;
if(operand->type->type != TYPE_TYPE_PRIMITIVE) {
stahp_token(&P->tokens[P->i], "Invalid combination of operator and operand types.");
}
binop_implicit_cast(astop);
ret = (AST*) astop;
}
}
return ret;
} else if(lOP == 0) {
AST *ret = nct_parse_expression(P, lOP + 1);
if(peek(P, 0).type == TOKEN_DOUBLE_EQUALS || peek(P, 0).type == TOKEN_EXCLAMATION_EQUALS || peek(P, 0).type == TOKEN_LESS || peek(P, 0).type == TOKEN_GREATER || peek(P, 0).type == TOKEN_LEQUAL || peek(P, 0).type == TOKEN_GEQUAL) {
while(1) {
BinaryOp op;
@ -623,6 +602,33 @@ AST *nct_parse_expression(Parser *P, int lOP) {
else if(maybe(P, TOKEN_GEQUAL)) op = BINOP_GEQUAL;
else break;
ASTExprBinaryOp *astop = alloc_node(P, sizeof(*astop));
astop->nodeKind = AST_EXPR_BINARY_OP;
astop->type = type_u(1);
astop->operator = op;
astop->operands[0] = ret;
ASTExpr *operand = &(astop->operands[1] = nct_parse_expression(P, lOP + 1))->expression;
if(operand->type->type != TYPE_TYPE_PRIMITIVE) {
stahp_token(&P->tokens[P->i], "Invalid combination of operator and operand types.");
}
ret = (AST*) astop;
}
}
return ret;
} else if(lOP == 0) {
AST *ret = nct_parse_expression(P, lOP + 1);
if(peek(P, 0).type == TOKEN_DOUBLE_AMPERSAND || peek(P, 0).type == TOKEN_DOUBLE_VERTICAL_BAR) {
while(1) {
BinaryOp op;
if(maybe(P, TOKEN_DOUBLE_AMPERSAND)) op = BINOP_LOGICAL_AND;
else if(maybe(P, TOKEN_DOUBLE_VERTICAL_BAR)) op = BINOP_LOGICAL_OR;
else break;
ASTExprBinaryOp *astop = alloc_node(P, sizeof(*astop));
astop->nodeKind = AST_EXPR_BINARY_OP;
astop->type = NULL;
@ -1160,7 +1166,7 @@ static void nct_parse_statement(Parser *P) {
ret->next = NULL;
if(!maybe(P, TOKEN_SEMICOLON)) {
ret->val = nct_parse_expression(P, 0);
ret->val = ast_cast_expr(nct_parse_expression(P, 0), P->topLevel->functionType->function.ret);
expect(P, TOKEN_SEMICOLON);
}
@ -1642,6 +1648,8 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize, Scope *t
/* Arguments */
if(ft && isTopLevel) {
P->topLevel->functionType = ft;
ScopeItem **vtes = alloca(sizeof(*vtes) * ft->function.argCount);
// First arguments in a function TLC is the arguments

View File

@ -6,6 +6,8 @@
#include<stdbool.h>
#include<string.h>
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef enum {
TYPE_TYPE_PRIMITIVE, TYPE_TYPE_RECORD, TYPE_TYPE_POINTER, TYPE_TYPE_FUNCTION, TYPE_TYPE_ARRAY, TYPE_TYPE_GENERIC, TYPE_TYPE_ERROR
@ -117,6 +119,21 @@ static inline bool type_is_segmented_pointer(Type *type) {
return type->type == TYPE_TYPE_RECORD && !!strstr(type->record.name, " @far*");
}
static inline bool type_is_any_pointer(Type *type) {
return type->type == TYPE_TYPE_POINTER || type_is_segmented_pointer(type);
}
static inline Type *type_dereference(Type *type) {
assert(type_is_any_pointer(type));
if(type->type == TYPE_TYPE_POINTER) {
return type->pointer.of;
} else if(type_is_segmented_pointer(type)) {
return type->record.fieldTypes[1]->pointer.of;
}
abort();
}
static inline Type *type_u(size_t bits) {
char buf[32];
snprintf(buf, sizeof(buf), "u%lu", bits);

View File

@ -142,7 +142,7 @@ static AST *is_field_access(AST *e) {
* */
static const char *xop_sz(AST *tlc, AST *e, int sz) {
#define XOPBUFS 16
#define XOPBUFSZ 32
#define XOPBUFSZ 64
static char bufs[XOPBUFS][XOPBUFSZ];
static int bufidx = 0;

View File

@ -109,6 +109,10 @@ static void mark_d(ScopeItem *si) {
}
}
/*
* RULE ONE OF DUMBING: NEVER xopify NOR varify MORE THAN ONCE IN A SINGLE CALL TO VISITOR!!!
* IF YOU DO THIS, stmtPrev WILL FUCK UP AND STATEMENTS WILL BE LOST
*/
struct DumbenState {
AST *targetTLC;
int effective;
@ -129,14 +133,10 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
if(is_xop(e->exprBinOp.operands[0]) == XOP_NOT_XOP) {
e->exprBinOp.operands[0] = xopify(tlc, chu, stmtPrev, s, e->exprBinOp.operands[0]);
this->effective = 1;
}
if(is_xop(e->exprBinOp.operands[1]) == XOP_NOT_XOP) {
} else if(is_xop(e->exprBinOp.operands[1]) == XOP_NOT_XOP) {
e->exprBinOp.operands[1] = xopify(tlc, chu, stmtPrev, s, e->exprBinOp.operands[1]);
this->effective = 1;
}
if(is_xop(e->exprBinOp.operands[0]) == XOP_MEM && is_xop(e->exprBinOp.operands[1]) == XOP_MEM) {
} else if(is_xop(e->exprBinOp.operands[0]) == XOP_MEM && is_xop(e->exprBinOp.operands[1]) == XOP_MEM) {
// Can't have two mems; put one in var
e->exprBinOp.operands[1] = varify(tlc, chu, stmtPrev, s, e->exprBinOp.operands[1]);