From 089b01e0849b9eb01b5ab542b0baabe2d6ce1865 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Mon, 14 Oct 2019 19:20:46 -0400 Subject: made parameters immutable; fixed other things involving immutability --- abbrevs.txt | 1 + build.sh | 10 +++---- cgen.c | 16 +++-------- eval.c | 4 +-- main.c | 1 + out.c | 45 ++++++++----------------------- parse.c | 33 ++++++++++++++++++++--- test.toc | 37 ++++++++----------------- tokenizer.c | 4 +-- types.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- types.h | 28 ++++++++++++++++--- 11 files changed, 175 insertions(+), 94 deletions(-) diff --git a/abbrevs.txt b/abbrevs.txt index d907681..49d29ce 100644 --- a/abbrevs.txt +++ b/abbrevs.txt @@ -11,6 +11,7 @@ expr - expression fn - function ident - identifier kw - keyword +mut - mutable num - number op - operator param - parameter diff --git a/build.sh b/build.sh index 619252b..9290a15 100755 --- a/build.sh +++ b/build.sh @@ -1,5 +1,5 @@ -#!/bin/bash -if [[ $CC == "" ]]; then +#!/bin/sh +if [ "$CC" = "" ]; then CC=gcc fi @@ -12,10 +12,10 @@ fi ADDITIONAL_FLAGS='-Wno-unused-function' -if [[ $CC == "clang" ]]; then - WARNINGS='-Wall -Wextra -Wpedantic -Wshadow -Wimplicit-fallthrough -Wno-unused-parameter' +if [ "$CC" = "clang" ]; then + WARNINGS='-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wimplicit-fallthrough -Wno-unused-parameter' else - WARNINGS='-Wall -Wextra -Wpedantic -Wshadow -Wno-pointer-to-int-cast -Wno-unused-parameter' + WARNINGS='-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wno-pointer-to-int-cast -Wno-unused-parameter' fi DEBUG_FLAGS="-O0 -g3 $WARNINGS -std=c11 -DTOC_DEBUG" diff --git a/cgen.c b/cgen.c index 0650ca2..27fb0cf 100644 --- a/cgen.c +++ b/cgen.c @@ -1,13 +1,3 @@ -typedef struct { - FILE *outc; - unsigned long ident_counter; - ParsedFile *file; - Block *block; - FnExpr *fn; /* which function are we in? (NULL for none) - not used during decls */ - Evaluator *evalr; - Identifier main_ident; -} CGenerator; - static bool cgen_stmt(CGenerator *g, Statement *s); static bool cgen_block(CGenerator *g, Block *b); static bool cgen_expr(CGenerator *g, Expression *e); @@ -89,8 +79,8 @@ static void cgen_ident(CGenerator *g, Identifier i) { } } -static void cgen_ident_id(CGenerator *g, unsigned long id) { - cgen_write(g, "a%lu_", id); +static void cgen_ident_id(CGenerator *g, IdentID id) { + cgen_write(g, "a%lu_", (unsigned long)id); } static bool cgen_type_post(CGenerator *g, Type *t, Location where); @@ -522,7 +512,7 @@ static bool cgen_decl(CGenerator *g, Declaration *d) { cgen_write(g, "{"); cgen_nl(g); if (!cgen_type_pre(g, &d->expr.type, d->expr.where)) return false; - cgen_write(g, "expr__"); + cgen_write(g, " expr__"); if (!cgen_type_post(g, &d->expr.type, d->expr.where)) return false; cgen_write(g, "; "); if (!cgen_set(g, NULL, "expr__", &d->expr, NULL)) diff --git a/eval.c b/eval.c index 71f8888..b9d948f 100644 --- a/eval.c +++ b/eval.c @@ -436,7 +436,7 @@ static bool eval_expr(Evaluator *ev, Expression *e, Value *v) { /* WARNING: macros ahead */ #define eval_unary_op_one(low, up, op) \ case BUILTIN_##up: \ - v->low = op of.low; break + v->low = (up)(op of.low); break #define eval_unary_op_nums(builtin, op) \ eval_unary_op_one(i8, I8, op); \ eval_unary_op_one(i16, I16, op); \ @@ -457,7 +457,7 @@ static bool eval_expr(Evaluator *ev, Expression *e, Value *v) { #define eval_binary_op_one(low, up, op) \ case BUILTIN_##up: \ - v->low = lhs.low op rhs.low; break + v->low = (up)(lhs.low op rhs.low); break #define eval_binary_op_nums(builtin, op) \ eval_binary_op_one(i8, I8, op); \ diff --git a/main.c b/main.c index 00b95a7..05a41b6 100644 --- a/main.c +++ b/main.c @@ -3,6 +3,7 @@ TODO: re-do cgen arrs are pointers make sure initializers for global variables are compile-time constants +allow, e.g.: x := "foo"; x[0] = 'g'; any odd number of "s for a string */ #include "toc.c" diff --git a/out.c b/out.c index 2bfb426..024b9fa 100644 --- a/out.c +++ b/out.c @@ -16,8 +16,8 @@ typedef unsigned char bool; /* declarations */ -void puti(i64 i); -void asdf(i64 i, i64( (*ret__)[3])); +i64 puti(i64 i); +void dbl(i64((* x)[3])); void main__(); /* code */ int main() { @@ -25,43 +25,20 @@ int main() { return 0; } -void puti(i64 i) { +i64 puti(i64 i) { printf("%ld\n", i); } -void asdf(i64 i, i64( (*ret__)[3])) { -i64( ret[3]) = {0}; -i64( asdf[3]) = {0}; -(ret[0]) = (0*i);; -(ret[1]) = (1*i);; -(ret[2]) = (2*i);; -{ -size_t i;i64(*arr__in) = asdf; i64(*arr__out) = ret; -for (i = 0; i < 3; i++) arr__out[i] = arr__in[i]; -}; -{ -size_t i;i64(*arr__in) = *ret__; i64(*arr__out) = asdf; -for (i = 0; i < 3; i++) arr__out[i] = arr__in[i]; -}return; +void dbl(i64((* x)[3])) { +((*x)[0]) = (((*x)[0])*2);; +((*x)[1]) = (((*x)[1])*2);; +((*x)[2]) = (((*x)[2])*2);; } void main__() { -i64(* x); { -i64(*expr__); expr__ = ((i64(*))calloc(1, sizeof(i64)));x = expr__;} -(*x) = 17;; -if (((*x)==0)) { -(*x) = ((1+2)+(3-(5/62)));; -} else { -(*x) = ((4+5)+6);; -}; -(puti((*x))); -(free(x)); -void (* fptr)(i64, i64((*)[3])); { -void (*expr__)(i64, i64((*)[3])); expr__ = asdf;fptr = expr__;} +i64( a[3]) = {0}; +(a[0]) = 1;; +(dbl((&a))); +(puti((a[0]))); } -i64 foo = 5; -char( bar[5]) = "\x48\x65\x6c\x6c\x6f"; -i64 a = 123; i64 b = 123; -char x = ((char)97); -i64 sadkfj = (-1293812); diff --git a/parse.c b/parse.c index f949794..01f8096 100644 --- a/parse.c +++ b/parse.c @@ -3,6 +3,30 @@ static bool parse_expr(Parser *p, Expression *e, Token *end); #define PARSE_DECL_ALLOW_CONST_WITH_NO_EXPR 0x01 static bool parse_decl(Parser *p, Declaration *d, DeclEndKind ends_with, uint16_t flags); +static const char *expr_kind_to_str(ExprKind k) { + switch (k) { + case EXPR_LITERAL_FLOAT: return "float literal"; + case EXPR_LITERAL_INT: return "integer literal"; + case EXPR_LITERAL_STR: return "string literal"; + case EXPR_LITERAL_BOOL: return "boolean literal"; + case EXPR_LITERAL_CHAR: return "character literal"; + case EXPR_IF: return "if expression"; + case EXPR_WHILE: return "while expression"; + case EXPR_CALL: return "function call"; + case EXPR_DIRECT: return "directive"; + case EXPR_NEW: return "new expression"; + case EXPR_CAST: return "cast expression"; + case EXPR_UNARY_OP: return "unary operator"; + case EXPR_BINARY_OP: return "binary operator"; + case EXPR_FN: return "function expression"; + case EXPR_TUPLE: return "tuple"; + case EXPR_BLOCK: return "block"; + case EXPR_IDENT: return "identifier"; + } + assert(0); + return ""; +} + static const char *unary_op_to_str(UnaryOp u) { switch (u) { case UNARY_MINUS: return "-"; @@ -459,7 +483,8 @@ static bool parse_block(Parser *p, Block *b) { } static bool is_decl(Tokenizer *t); - +static inline bool ends_decl(Token *t, DeclEndKind ends_with); + static bool parse_decl_list(Parser *p, Declaration **decls, DeclEndKind decl_end) { Tokenizer *t = p->tokr; bool ret = true; @@ -472,8 +497,8 @@ static bool parse_decl_list(Parser *p, Declaration **decls, DeclEndKind decl_end Declaration *decl = parser_arr_add(p, decls); if (!parse_decl(p, decl, decl_end, PARSE_DECL_ALLOW_CONST_WITH_NO_EXPR)) { ret = false; - /* skip to end of param list */ - while (t->token->kind != TOKEN_EOF && !token_is_kw(t->token, KW_RPAREN)) + /* skip to end of list */ + while (t->token->kind != TOKEN_EOF && !ends_decl(t->token, decl_end)) t->token++; break; } @@ -499,6 +524,8 @@ static bool parse_fn_expr(Parser *p, FnExpr *f) { } else { if (!parse_decl_list(p, &f->params, DECL_END_RPAREN_COMMA)) return false; + arr_foreach(f->params, Declaration, pdecl) + pdecl->flags |= DECL_FLAG_PARAM; } if (t->token->kind == TOKEN_EOF) { diff --git a/test.toc b/test.toc index 1aeca39..148c6de 100644 --- a/test.toc +++ b/test.toc @@ -1,33 +1,18 @@ -puti @= fn(i: int) { +puti @= fn(i: int) int { #C("printf(\"%ld\\n\", i)"); + i }; -asdf @= fn(i: int) [3]int { - ret : [3]int; - asdf : [3]int; - ret[0] = 0*i; - ret[1] = 1*i; - ret[2] = 2*i; - asdf = ret; - return asdf; +dbl @= fn(x: &[3]int) { + (*x)[0] = (*x)[0] * 2; + (*x)[1] = (*x)[1] * 2; + (*x)[2] = (*x)[2] * 2; }; main @= fn() { - x := new int; - *x = 17; - if *x == 0 { - *x = 1+2+3-5/62; - } else { - *x = 4+5+6; - } - puti(*x); -del x; - fptr := asdf; -}; - -foo := 5; -bar := "Hello"; -a, b := 123; -x := 'a'; -sadkfj := -1293812; + a : [3]int; + a[0] = 1; + dbl(&a); + puti(a[0]); +}; \ No newline at end of file diff --git a/tokenizer.c b/tokenizer.c index a665ca5..1a4f5dd 100644 --- a/tokenizer.c +++ b/tokenizer.c @@ -374,7 +374,7 @@ static bool tokenize_string(Tokenizer *t, char *str) { switch (n.kind) { case NUM_LITERAL_INT: if (n.intval > UINTEGER_MAX / (UInteger)base || - n.intval * (UInteger)base > ULLONG_MAX - (UInteger)digit) { + n.intval * (UInteger)base > UINT_MAX - (UInteger)digit) { /* too big! */ tokenization_err(t, "Number too big to fit in a numeric literal."); goto err; @@ -464,7 +464,7 @@ static bool tokenize_string(Tokenizer *t, char *str) { } *strptr = 0; token->kind = TOKEN_STR_LITERAL; - token->str.len = strptr - strlit; + token->str.len = (size_t)(strptr - strlit); token->str.str = strlit; tokr_nextchar(t); /* move past closing " */ continue; diff --git a/types.c b/types.c index 8426b3b..39de36a 100644 --- a/types.c +++ b/types.c @@ -80,7 +80,61 @@ static bool type_must_eq(Location where, Type *expected, Type *got) { return true; } -/* sometimes prints an error and returns false if the given expression is not an l-value */ + +/* +this expression, which is an array, must be mutable (otherwise print an error, +return false)! +*/ +static bool expr_arr_must_mut(Expression *e) { + switch (e->kind) { + case EXPR_IDENT: { + IdentDecl *idecl = ident_decl(e->ident); + Declaration *d = idecl->decl; + if (d->flags & DECL_FLAG_CONST) { + err_print(e->where, "Cannot modify a constant array."); + return false; + } + if (d->flags & DECL_FLAG_PARAM) { + err_print(e->where, "Parameters are immutable."); + return false; + } + } return true; + case EXPR_CAST: + case EXPR_CALL: + case EXPR_NEW: + case EXPR_UNARY_OP: + return true; + case EXPR_WHILE: + assert(e->while_.body.ret_expr); + return expr_arr_must_mut(e->while_.body.ret_expr); + case EXPR_IF: + for (IfExpr *i = &e->if_; i; i->next_elif ? i = &i->next_elif->if_ : (i = NULL)) { + assert(i->body.ret_expr); + if (!expr_arr_must_mut(i->body.ret_expr)) + return false; + } + return true; + case EXPR_BLOCK: + assert(e->block.ret_expr); + return expr_arr_must_mut(e->block.ret_expr); + case EXPR_LITERAL_STR: + err_print(e->where, "String constants are immutable."); + return false; + case EXPR_LITERAL_BOOL: + case EXPR_FN: + case EXPR_TUPLE: + case EXPR_LITERAL_FLOAT: + case EXPR_LITERAL_CHAR: + case EXPR_LITERAL_INT: + case EXPR_BINARY_OP: + case EXPR_DIRECT: + break; + } + assert(0); + return false; +} + +/* prints an error and returns false if the given expression is not an l-value */ static bool expr_must_lval(Expression *e) { /* NOTE: make sure you update eval when you change this */ switch (e->kind) { @@ -94,6 +148,11 @@ static bool expr_must_lval(Expression *e) { info_print(d->where, "%s was declared here.", istr); return false; } + if (d->flags & DECL_FLAG_PARAM) { + char *istr = ident_to_str(e->ident); + err_print(e->where, "You cannot modify or take the address of a parameter (%s).", istr); + return false; + } return true; } @@ -102,7 +161,7 @@ static bool expr_must_lval(Expression *e) { break; case EXPR_BINARY_OP: switch (e->binary.op) { - case BINARY_AT_INDEX: return true; + case BINARY_AT_INDEX: return expr_arr_must_mut(e->binary.lhs); default: break; } break; @@ -114,9 +173,24 @@ static bool expr_must_lval(Expression *e) { } return true; } break; - default: - break; + case EXPR_CAST: + case EXPR_NEW: + case EXPR_FN: + case EXPR_LITERAL_FLOAT: + case EXPR_LITERAL_CHAR: + case EXPR_LITERAL_STR: + case EXPR_LITERAL_INT: + case EXPR_LITERAL_BOOL: + case EXPR_IF: + case EXPR_WHILE: + case EXPR_CALL: + case EXPR_DIRECT: + case EXPR_BLOCK: { + err_print(e->where, "Cannot use %s as l-value.", expr_kind_to_str(e->kind)); + return false; } + } + assert(0); return false; } @@ -622,11 +696,16 @@ static bool types_expr(Typer *tr, Expression *e) { if (!types_expr(tr, &arg->val)) return false; } + if (f->type.kind == TYPE_UNKNOWN) { + e->type.kind = TYPE_UNKNOWN; + return true; + } if (f->type.kind != TYPE_FN) { char *type = type_to_str(&f->type); err_print(e->where, "Calling non-function (type %s).", type); return false; } + Type *ret_type = f->type.fn.types; Type *param_types = ret_type + 1; Argument *args = c->args; @@ -831,7 +910,6 @@ static bool types_expr(Typer *tr, Expression *e) { switch (e->binary.op) { case BINARY_SET: if (!expr_must_lval(e->binary.lhs)) { - err_print(e->where, "You can only assign to an lvalue."); /* FEATURE: better err */ return false; } /* fallthrough */ @@ -988,7 +1066,7 @@ static bool types_decl(Typer *tr, Declaration *d) { goto ret; } d->type = d->expr.type; - d->type.flags &= ~TYPE_FLAG_FLEXIBLE; /* x := 5; => x is not flexible */ + d->type.flags &= (uint16_t)~(uint16_t)TYPE_FLAG_FLEXIBLE; /* x := 5; => x is not flexible */ } if (d->flags & DECL_FLAG_CONST) { if (!(d->flags & DECL_FLAG_FOUND_VAL)) { diff --git a/types.h b/types.h index 6c65a0a..f21ff3f 100644 --- a/types.h +++ b/types.h @@ -25,6 +25,8 @@ typedef int64_t I64; typedef float F32; typedef double F64; +typedef U32 IdentID; /* identifier ID for cgen (anonymous variables) */ + typedef uint32_t LineNo; typedef struct { LineNo line; @@ -342,11 +344,17 @@ typedef struct { typedef struct { struct Expression *cond; /* NULL = this is an else */ struct Expression *next_elif; /* next elif/else of this statement */ + struct { + IdentID id; + } c; Block body; } IfExpr; typedef struct { struct Expression *cond; + struct { + IdentID id; + } c; Block body; } WhileExpr; @@ -358,7 +366,7 @@ typedef struct FnExpr { struct { /* if name = NULL, this is an anonymous function, and id will be the ID of the fn. */ Identifier name; - unsigned long id; + IdentID id; } c; } FnExpr; /* an expression such as fn(x: int) int { 2 * x } */ @@ -370,12 +378,13 @@ typedef struct { #define EXPR_FLAG_FOUND_TYPE 0x01 typedef struct Expression { + Type type; Location where; ExprKind kind; uint16_t flags; - Type type; union { Floating floatl; + /* Floating floatl; */ UInteger intl; StrLiteral strl; bool booll; @@ -402,7 +411,10 @@ typedef struct Expression { WhileExpr while_; FnExpr fn; CastExpr cast; - Block block; + struct { + Block block; + IdentID block_ret_id; + }; struct Expression *tuple; }; } Expression; @@ -419,6 +431,7 @@ typedef struct Argument { #define DECL_FLAG_FOUND_TYPE 0x08 #define DECL_FLAG_ERRORED_ABOUT_SELF_REFERENCE 0x10 /* has there been an error about this decl referencing itself? */ #define DECL_FLAG_FOUND_VAL 0x20 +#define DECL_FLAG_PARAM 0x40 /* is this a parameter declaration? (needed because parameters are immutable) */ /* OPTIM: Instead of using dynamic arrays, do two passes. */ typedef struct Declaration { @@ -483,3 +496,12 @@ typedef struct Typer { Type ret_type; /* the return type of the function we're currently parsing. */ } Typer; +typedef struct { + FILE *outc; + IdentID ident_counter; + ParsedFile *file; + Block *block; + FnExpr *fn; /* which function are we in? (NULL for none) - not used during decls */ + Evaluator *evalr; + Identifier main_ident; +} CGenerator; -- cgit v1.2.3