From 8ce406bdb55c19d83abb8f7511212ee0c02cb487 Mon Sep 17 00:00:00 2001
From: Leo Tenenbaum <pommicket@gmail.com>
Date: Thu, 31 Oct 2019 13:28:39 -0400
Subject: basic . operator

---
 cgen.c          | 11 ++++++++-
 decls_cgen.c    |  7 ++++--
 eval.c          | 16 ++++++++++---
 identifiers.c   | 18 +++++++++++++++
 main.c          | 10 +++++---
 parse.c         | 42 ++++++++++++++++++++++-----------
 test.toc        | 17 +++++++++-----
 tokenizer.c     |  2 +-
 typedefs_cgen.c |  7 ++++--
 types.c         | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 types.h         |  7 ++++--
 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;
-- 
cgit v1.2.3