summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2019-10-31 13:28:39 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2019-10-31 13:28:39 -0400
commit8ce406bdb55c19d83abb8f7511212ee0c02cb487 (patch)
tree837e2f0c7b4fb6621ab81ad9cad415a2c2035b0b
parentfc6c746a726b3a56f2489c3db884c8ac2cd491d7 (diff)
basic . operator
-rw-r--r--cgen.c11
-rw-r--r--decls_cgen.c7
-rw-r--r--eval.c16
-rw-r--r--identifiers.c18
-rw-r--r--main.c10
-rw-r--r--parse.c42
-rw-r--r--test.toc17
-rw-r--r--tokenizer.c2
-rw-r--r--typedefs_cgen.c7
-rw-r--r--types.c72
-rw-r--r--types.h7
11 files changed, 168 insertions, 41 deletions
diff --git a/cgen.c b/cgen.c
index a498bc3..22ac57d 100644
--- a/cgen.c
+++ b/cgen.c
@@ -698,7 +698,8 @@ static bool cgen_expr_pre(CGenerator *g, Expression *e) {
break;
case EXPR_BINARY_OP:
if (!cgen_expr_pre(g, e->binary.lhs)) return false;
- if (!cgen_expr_pre(g, e->binary.rhs)) return false;
+ if (e->binary.op != BINARY_DOT)
+ if (!cgen_expr_pre(g, e->binary.rhs)) return false;
break;
case EXPR_CAST:
if (!cgen_expr_pre(g, e->cast.expr)) return false;
@@ -862,6 +863,14 @@ static bool cgen_expr(CGenerator *g, Expression *e) {
cgen_write(g, ")");
handled = true;
break;
+ case BINARY_DOT:
+ cgen_write(g, "(");
+ cgen_expr(g, e->binary.lhs);
+ cgen_write(g, ".");
+ cgen_ident(g, e->binary.field->name);
+ cgen_write(g, ")");
+ handled = true;
+ break;
}
if (handled) break;
cgen_write(g, "(");
diff --git a/decls_cgen.c b/decls_cgen.c
index dcf5629..0a7a521 100644
--- a/decls_cgen.c
+++ b/decls_cgen.c
@@ -8,8 +8,11 @@ static bool cgen_decls_expr(CGenerator *g, Expression *e) {
return false;
break;
case EXPR_BINARY_OP:
- if (!cgen_decls_expr(g, e->binary.lhs)
- || !cgen_decls_expr(g, e->binary.rhs))
+ if (!cgen_decls_expr(g, e->binary.lhs))
+ return false;
+
+ if (e->binary.op != BINARY_DOT)
+ if (!cgen_decls_expr(g, e->binary.rhs))
return false;
break;
case EXPR_CAST:
diff --git a/eval.c b/eval.c
index eb52428..18ad9e9 100644
--- a/eval.c
+++ b/eval.c
@@ -864,12 +864,22 @@ static bool eval_expr(Evaluator *ev, Expression *e, Value *v) {
} break;
case EXPR_BINARY_OP: {
Value lhs, rhs;
- /* TODO(eventually): short-circuiting */
+ Expression *lhs_expr = e->binary.lhs, *rhs_expr = e->binary.rhs;
if (e->binary.op != BINARY_SET)
- if (!eval_expr(ev, e->binary.lhs, &lhs)) return false;
- if (!eval_expr(ev, e->binary.rhs, &rhs)) return false;
+ if (!eval_expr(ev, lhs_expr, &lhs)) return false;
+ if (e->binary.op != BINARY_DOT)
+ if (!eval_expr(ev, rhs_expr, &rhs)) return false;
+
BuiltinType builtin = e->binary.lhs->type.builtin;
switch (e->binary.op) {
+ case BINARY_DOT: {
+ Type *inner_type = &e->binary.lhs->type;
+ while (inner_type->kind == TYPE_USER)
+ inner_type = ident_typeval(inner_type->user.name);
+ eval_struct_find_offsets(inner_type);
+
+ eval_deref(v, (char *)lhs.struc + e->binary.field->offset, &e->type);
+ } break;
case BINARY_ADD:
if (e->binary.lhs->type.kind == TYPE_PTR) {
v->ptr = (char *)lhs.ptr + val_to_i64(&rhs, e->binary.rhs->type.builtin)
diff --git a/identifiers.c b/identifiers.c
index c29f8ce..0465e12 100644
--- a/identifiers.c
+++ b/identifiers.c
@@ -200,3 +200,21 @@ static inline Type *ident_typeval(Identifier i) {
if (!val) return NULL;
return val->type;
}
+
+static bool ident_eq_str(Identifier i, const char *s) {
+ const char *t = s + (strlen(s) - 1);
+ while (1) {
+ if (!i->parent) {
+ return false;
+ }
+ int c_low = i->parent->index_in_parent;
+ int c_high = i->index_in_parent;
+ int c = ident_uchar_to_char(c_low + (c_high << 4));
+ if (c != *t) return false;
+ i = i->parent->parent;
+ if (t > s) t--;
+ else break;
+ }
+ if (i->parent) return false; /* not at root */
+ return true;
+}
diff --git a/main.c b/main.c
index ab17fe6..0ffb8e6 100644
--- a/main.c
+++ b/main.c
@@ -1,15 +1,19 @@
/*
TODO:
-dot
+dot at run time
+dot at compile time
+dot + string
+. lvalue
+using dot with pointers
length of slice/arr with .len
verify size of struct, align of fields
pointers to futurely-declared types
don't allow while {3; 5} (once break is added)
-allow omission of trailing ; in foo @= fn() {}
any odd number of "s for a string
modifiable strings:
s := ["sakjdfhkjh ksjdahfkjsd ahs ahdf hsdaf khsadkjfh"];
-unicode variable names
+unicode variable names (cgen support)
+allow omission of trailing ; in foo @= fn() {}?
*/
#include "toc.c"
diff --git a/parse.c b/parse.c
index 78ad636..5f6a506 100644
--- a/parse.c
+++ b/parse.c
@@ -55,6 +55,7 @@ static const char *binary_op_to_str(BinaryOp b) {
case BINARY_GE: return ">=";
case BINARY_EQ: return "==";
case BINARY_NE: return "!=";
+ case BINARY_DOT: return ".";
}
assert(0);
return "";
@@ -617,18 +618,21 @@ static bool parser_is_definitely_type(Parser *p, Token **end) {
if (paren_level == 0) {
t->token++;
if (token_is_kw(t->token, KW_LBRACE)) goto end; /* void fn expr */
- Token *child_end;
- if (parser_is_definitely_type(p, &child_end)) { /* try return type */
- if (token_is_kw(t->token, KW_LBRACE)) {
- /* non-void fn expr */
- goto end;
- }
+ Type return_type;
+ t->token->where.ctx->enabled = false;
+ if (!parse_type(p, &return_type)) {
+ /* couldn't parse a return type. void fn type */
+ ret = true;
+ goto end;
}
- /* it's a function type! */
+
+ if (token_is_kw(t->token, KW_LBRACE)) {
+ /* non-void fn expr */
+ goto end;
+ }
+ /* it's a non-void function type */
ret = true;
goto end;
-
-
}
break;
default: break;
@@ -847,7 +851,7 @@ static void fprint_expr(FILE *out, Expression *e);
#define NOT_AN_OP -1
/* cast/new aren't really operators since they operate on types, not exprs. */
-#define CAST_PRECEDENCE 2
+#define CAST_PRECEDENCE 45
#define NEW_PRECEDENCE 22
static int op_precedence(Keyword op) {
switch (op) {
@@ -865,6 +869,7 @@ static int op_precedence(Keyword op) {
case KW_ASTERISK: return 30;
case KW_SLASH: return 40;
case KW_EXCLAMATION: return 50;
+ case KW_DOT: return 60;
case KW_DEL: return 1000;
default: return NOT_AN_OP;
}
@@ -1291,6 +1296,9 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) {
case KW_SLASH:
op = BINARY_DIV;
break;
+ case KW_DOT:
+ op = BINARY_DOT;
+ break;
case KW_AMPERSAND:
case KW_EXCLAMATION:
case KW_DEL:
@@ -1794,14 +1802,20 @@ static void fprint_expr(FILE *out, Expression *e) {
case EXPR_IDENT:
fprint_ident(out, e->ident);
break;
- case EXPR_BINARY_OP:
- fprintf(out, "%s", binary_op_to_str(e->binary.op));
+ case EXPR_BINARY_OP: {
+ bool prev = parse_printing_after_types;
fprintf(out, "(");
fprint_expr(out, e->binary.lhs);
- fprintf(out, ",");
+ fprintf(out, ")%s(", binary_op_to_str(e->binary.op));
+ if (e->binary.op == BINARY_DOT) {
+ parse_printing_after_types = false; /* don't show types for rhs of . */
+ }
fprint_expr(out, e->binary.rhs);
+ if (e->binary.op == BINARY_DOT) {
+ parse_printing_after_types = prev;
+ }
fprintf(out, ")");
- break;
+ } break;
case EXPR_UNARY_OP:
fprintf(out, "%s", unary_op_to_str(e->unary.op));
fprintf(out, "(");
diff --git a/test.toc b/test.toc
index fa54fc3..26b449d 100644
--- a/test.toc
+++ b/test.toc
@@ -4,14 +4,19 @@ puti @= fn(x: int) {
};
Point @= struct {
- x, y : int;
- something:fn(f32);
- z,asdfasdfasdf:(int,int);
-
+ x_coordinate, y_coordinate : int;
};
main @= fn() {
p:Point;
-a:p;
- // asasdfdsfa:(int,int);
+ x := p.({
+ t @= int;
+ f @= fn() t { 7 as t };
+ if f() as int > 3 {
+ "x_coordinate"
+ } else {
+ "y_coordinate"
+ }
+ });
+ puti(x);
};
diff --git a/tokenizer.c b/tokenizer.c
index e6412a8..7ec5f1a 100644
--- a/tokenizer.c
+++ b/tokenizer.c
@@ -1,6 +1,6 @@
static const char *keywords[KW_COUNT] =
{";", ":", "@", ",", "(", ")", "{", "}", "[", "]", "==", "!=", "<=", "<", ">=", ">",
- "+", "-", "*", "!", "&", "/",
+ "+", "-", "*", "!", "&", "/", ".",
"=",
"if", "elif", "else", "while", "return", "fn", "as",
"new", "del", "struct",
diff --git a/typedefs_cgen.c b/typedefs_cgen.c
index d6a3f5e..47122e0 100644
--- a/typedefs_cgen.c
+++ b/typedefs_cgen.c
@@ -17,9 +17,12 @@ static bool typedefs_expr(CGenerator *g, Expression *e) {
return false;
break;
case EXPR_BINARY_OP:
- if (!typedefs_expr(g, e->binary.lhs)
- || !typedefs_expr(g, e->binary.rhs))
+ if (!typedefs_expr(g, e->binary.lhs))
return false;
+
+ if (e->binary.op != BINARY_DOT)
+ if (!typedefs_expr(g, e->binary.rhs))
+ return false;
break;
case EXPR_CAST:
if (!typedefs_expr(g, e->cast.expr))
diff --git a/types.c b/types.c
index 1f7169f..8769907 100644
--- a/types.c
+++ b/types.c
@@ -16,6 +16,10 @@ static inline void *typer_arr_add_(Typer *tr, void **arr, size_t sz) {
return arr_adda_(arr, sz, &tr->allocr);
}
+static inline bool type_is_builtin(Type *t, BuiltinType b) {
+ return t->kind == TYPE_BUILTIN && t->builtin == b;
+}
+
#define typer_arr_add(tr, a) typer_arr_add_(tr, (void **)(a), sizeof **(a))
static bool type_eq(Type *a, Type *b) {
@@ -940,13 +944,16 @@ static bool types_expr(Typer *tr, Expression *e) {
Expression *rhs = e->binary.rhs;
Type *lhs_type = &lhs->type;
Type *rhs_type = &rhs->type;
- if (!types_expr(tr, lhs)
- || !types_expr(tr, rhs))
- return false;
- if (lhs_type->kind == TYPE_UNKNOWN || rhs_type->kind == TYPE_UNKNOWN) {
- return true;
+ BinaryOp o = e->binary.op;
+ if (o != BINARY_DOT) {
+ if (!types_expr(tr, lhs)
+ || !types_expr(tr, rhs))
+ return false;
+ if (lhs_type->kind == TYPE_UNKNOWN || rhs_type->kind == TYPE_UNKNOWN) {
+ return true;
+ }
}
- switch (e->binary.op) {
+ switch (o) {
case BINARY_SET:
if (!expr_must_lval(e->binary.lhs)) {
return false;
@@ -972,7 +979,6 @@ static bool types_expr(Typer *tr, Expression *e) {
case BINARY_GE:
case BINARY_EQ:
case BINARY_NE: {
- BinaryOp o = e->binary.op;
bool valid = false;
if (o == BINARY_SET) {
valid = type_eq(lhs_type, rhs_type);
@@ -1074,6 +1080,58 @@ static bool types_expr(Typer *tr, Expression *e) {
}
}
break;
+ case BINARY_DOT: {
+ if (!types_expr(tr, lhs)) return false;
+ Type *inner_type = lhs_type;
+ while (inner_type->kind == TYPE_USER)
+ inner_type = ident_typeval(inner_type->user.name);
+ if (inner_type->kind == TYPE_STRUCT) {
+ bool is_field = false;
+ if (rhs->kind == EXPR_IDENT) {
+ /* maybe accessing a field? */
+ arr_foreach(inner_type->struc.fields, Field, f) {
+ if (f->name == rhs->ident) {
+ is_field = true;
+ *t = *f->type;
+ e->binary.field = f;
+ }
+ }
+ }
+
+ if (!is_field) {
+ /* allow some_struct."foo" */
+ Value field_name;
+ if (!types_expr(tr, rhs)) return false;
+ if (rhs_type->kind != TYPE_SLICE || !type_is_builtin(rhs_type->slice, BUILTIN_CHAR)) {
+ err_print(e->where, "Invalid field of type %s.");
+ return false;
+
+ }
+ if (!eval_expr(tr->evalr, rhs, &field_name)) return false;
+ arr_foreach(inner_type->struc.fields, Field, f) {
+ if (ident_eq_str(f->name, field_name.slice.data)) {
+ is_field = true;
+ *t = *f->type;
+ e->binary.field = f;
+ }
+ }
+ if (!is_field) {
+ char *fstr = err_malloc(field_name.slice.n + 1);
+ memcpy(fstr, field_name.slice.data, field_name.slice.n);
+ fstr[field_name.slice.n] = 0; /* null-terminate */
+ char *typestr = type_to_str(lhs_type);
+ err_print(e->where, "%s is not a field of structure %s.", fstr, typestr);
+ free(fstr); free(typestr);
+ return false;
+ }
+ }
+ } else {
+ char *s = type_to_str(lhs_type);
+ err_print(e->where, "Operator . applied to type %s, which is not a structure.", s);
+ free(s);
+ return false;
+ }
+ } break;
} break;
} break;
case EXPR_TUPLE:
diff --git a/types.h b/types.h
index e8c739f..d89d6a8 100644
--- a/types.h
+++ b/types.h
@@ -165,6 +165,7 @@ typedef enum {
KW_EXCLAMATION,
KW_AMPERSAND,
KW_SLASH,
+ KW_DOT,
KW_EQ,
KW_LAST_SYMBOL = KW_EQ, /* last one entirely consisting of symbols */
KW_IF,
@@ -362,7 +363,8 @@ typedef enum {
BINARY_LE,
BINARY_EQ,
BINARY_NE,
- BINARY_AT_INDEX /* e.g. x[i] */
+ BINARY_AT_INDEX, /* e.g. x[i] */
+ BINARY_DOT
} BinaryOp;
typedef struct {
@@ -436,7 +438,7 @@ typedef struct Expression {
Type type;
Location where;
ExprKind kind;
- uint16_t flags;
+ U16 flags;
union {
Floating floatl;
/* Floating floatl; */
@@ -452,6 +454,7 @@ typedef struct Expression {
BinaryOp op;
struct Expression *lhs;
struct Expression *rhs;
+ Field *field; /* for . only */
} binary;
CallExpr call;
DirectExpr direct;