From bbd9bab7ad73b24cb97944649859054411463d81 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Sun, 19 Jan 2020 12:50:58 -0500 Subject: more work on #foreign --- cgen.c | 8 +++++- decls_cgen.c | 4 +++ eval.c | 9 ++++++- main.c | 2 ++ package.c | 87 ++++++++++++++++++++++++++++++++++++++++++----------------- parse.c | 33 ++++++++++++++++++----- sdecls_cgen.c | 4 +++ test.toc | 14 +++++++--- tokenizer.c | 60 ++++++++++++++++++++++++++++++++++------- types.c | 64 ++++++++++++++++++++++++++++++++++++++++++- types.h | 24 ++++++++++++++--- 11 files changed, 259 insertions(+), 50 deletions(-) diff --git a/cgen.c b/cgen.c index 1bf233b..250ddda 100644 --- a/cgen.c +++ b/cgen.c @@ -1185,7 +1185,7 @@ static bool cgen_expr(CGenerator *g, Expression *e) { if (isprint(*p) && *p != '"') cgen_write(g, "%c", *p); else - cgen_write(g, "\\x%x", *p); + cgen_write(g, "\\x%02x", *p); } cgen_write(g, "\", %lu)", (unsigned long)e->strl.len); } break; @@ -1766,6 +1766,9 @@ static bool cgen_val(CGenerator *g, Value v, Type *t, Location where) { } static bool cgen_decl(CGenerator *g, Declaration *d) { + if (d->flags & DECL_FOREIGN) + return true; /* already dealt with */ + int has_expr = d->flags & DECL_HAS_EXPR; if (cgen_fn_is_direct(g, d)) return true; /* dealt with in cgen_defs_ */ @@ -1965,6 +1968,9 @@ static bool cgen_defs_expr(CGenerator *g, Expression *e) { } static bool cgen_defs_decl(CGenerator *g, Declaration *d) { + if (d->flags & DECL_FOREIGN) { + return true; /* dealt with by decls_cgen */ + } if (d->flags & DECL_HAS_EXPR) { if (!cgen_defs_expr(g, &d->expr)) return false; diff --git a/decls_cgen.c b/decls_cgen.c index d9ba325..7e91e2a 100644 --- a/decls_cgen.c +++ b/decls_cgen.c @@ -188,6 +188,10 @@ static bool cgen_decls_block(CGenerator *g, Block *b) { } static bool cgen_decls_decl(CGenerator *g, Declaration *d) { + if (d->flags & DECL_FOREIGN) { + /* TODO */ + return true; + } if (!cgen_decls_type(g, &d->type)) return false; if (cgen_fn_is_direct(g, d)) { diff --git a/eval.c b/eval.c index 085e3f8..07607f2 100644 --- a/eval.c +++ b/eval.c @@ -1031,7 +1031,14 @@ static bool eval_ident(Evaluator *ev, Identifier ident, Value *v, Location where Declaration *d = NULL; if (is_decl) { d = idecl->decl; - + if (d->flags & DECL_FOREIGN) { + if (!(d->flags & DECL_FOUND_VAL)) { + err_print(where, "Cannot access foreign declaration at compile time (you can only call foreign functions)"); + return false; + } + v->fn = d->val.fn; + return true; + } if ((d->flags & DECL_FOUND_VAL) && type_is_builtin(&d->type, BUILTIN_TYPE) && d->val.type->kind == TYPE_STRUCT) { v->type = allocr_malloc(ev->allocr, sizeof *v->type); v->type->flags = TYPE_IS_RESOLVED; diff --git a/main.c b/main.c index c3a5bb9..fe5ea6a 100644 --- a/main.c +++ b/main.c @@ -19,6 +19,8 @@ /* TODO: C functions (#foreign) +foreign non-functions +no foreign parameter declarations variadic fns #include --- diff --git a/package.c b/package.c index 8147272..ae40160 100644 --- a/package.c +++ b/package.c @@ -149,6 +149,18 @@ static inline char *import_str(Importer *i, size_t len) { return str; } +static inline void export_cstr(Exporter *ex, const char *str) { + size_t len = strlen(str); + export_len(ex, len); + export_str(ex, str, len); +} + +static inline char *import_cstr(Importer *i) { + size_t len = import_len(i); + return import_str(i, len); +} + + static void export_location(Exporter *ex, Location where) { /* for now, we only export the line */ export_vlq(ex, (U64)where.start->pos.line); @@ -958,6 +970,13 @@ static bool export_decl(Exporter *ex, Declaration *d) { } else if (d->flags & DECL_HAS_EXPR) { if (!export_expr(ex, &d->expr)) return false; + } else if (d->flags & DECL_FOREIGN) { + if (!(d->flags & DECL_FOUND_TYPE)) { + if (!export_expr(ex, d->foreign.name)) + return false; + if (!export_expr(ex, d->foreign.lib)) + return false; + } } return true; } @@ -985,6 +1004,11 @@ static void import_decl(Importer *im, Declaration *d) { d->flags &= (DeclFlags)~(DeclFlags)DECL_HAS_EXPR; } else if (d->flags & DECL_HAS_EXPR) { import_expr(im, &d->expr); + } else if (d->flags & DECL_FOREIGN) { + if (!(d->flags & DECL_FOUND_TYPE)) { + d->foreign.name = import_expr_(im); + d->foreign.lib = import_expr_(im); + } } } @@ -1061,37 +1085,52 @@ static void import_block(Importer *im, Block *b) { } static bool export_fn(Exporter *ex, FnExpr *f) { - export_location(ex, f->where); - export_len(ex, arr_len(f->params)); - arr_foreach(f->params, Declaration, param) { - if (!export_decl(ex, param)) - return false; - arr_foreach(param->idents, Identifier, ident) { - export_ident_name(ex, *ident); + possibly_static_assert(sizeof f->flags == 1); + export_u8(ex, f->flags); + if (f->flags & FN_EXPR_FOREIGN) { + export_cstr(ex, f->foreign.name); + export_cstr(ex, f->foreign.lib); + } else { + + export_location(ex, f->where); + export_len(ex, arr_len(f->params)); + arr_foreach(f->params, Declaration, param) { + if (!export_decl(ex, param)) + return false; + arr_foreach(param->idents, Identifier, ident) { + export_ident_name(ex, *ident); + } } - } - if (!export_type(ex, &f->ret_type, f->where)) - return false; - export_len(ex, arr_len(f->ret_decls)); - arr_foreach(f->ret_decls, Declaration, ret_decl) - if (!export_decl(ex, ret_decl)) + if (!export_type(ex, &f->ret_type, f->where)) return false; - if (!export_block(ex, &f->body)) - return false; + export_len(ex, arr_len(f->ret_decls)); + arr_foreach(f->ret_decls, Declaration, ret_decl) + if (!export_decl(ex, ret_decl)) + return false; + if (!export_block(ex, &f->body)) + return false; + } return true; } static void import_fn(Importer *im, FnExpr *f) { - f->where = import_location(im); - import_arr(im, &f->params); - arr_foreach(f->params, Declaration, param) { - import_decl(im, param); + memset(f, 0, sizeof *f); + f->flags = import_u8(im); + if (f->flags & FN_EXPR_FOREIGN) { + f->foreign.name = import_cstr(im); + f->foreign.lib = import_cstr(im); + } else { + f->where = import_location(im); + import_arr(im, &f->params); + arr_foreach(f->params, Declaration, param) { + import_decl(im, param); + } + import_type(im, &f->ret_type); + import_arr(im, &f->ret_decls); + arr_foreach(f->ret_decls, Declaration, ret_decl) + import_decl(im, ret_decl); + import_block(im, &f->body); } - import_type(im, &f->ret_type); - import_arr(im, &f->ret_decls); - arr_foreach(f->ret_decls, Declaration, ret_decl) - import_decl(im, ret_decl); - import_block(im, &f->body); } static bool export_struct(Exporter *ex, StructDef *s) { diff --git a/parse.c b/parse.c index dd868a6..f969793 100644 --- a/parse.c +++ b/parse.c @@ -179,10 +179,6 @@ static Keyword builtin_type_to_kw(BuiltinType t) { return KW_COUNT; } -/* TODO: DELME */ -static void fprint_expr(FILE *out, Expression *expr); - - /* returns the number of characters written, not including the null character */ static size_t type_to_str_(Type *t, char *buffer, size_t bufsize) { switch (t->kind) { @@ -292,6 +288,10 @@ static inline void *parser_malloc(Parser *p, size_t bytes) { return allocr_malloc(p->allocr, bytes); } +static inline void *parser_calloc(Parser *p, size_t n, size_t bytes) { + return allocr_calloc(p->allocr, n, bytes); +} + /* allocate a new expression. */ static inline Expression *parser_new_expr(Parser *p) { return parser_malloc(p, sizeof(Expression)); @@ -1010,7 +1010,7 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) { case KW_FN: { /* this is a function */ e->kind = EXPR_FN; - if (!parse_fn_expr(p, e->fn = parser_malloc(p, sizeof *e->fn))) + if (!parse_fn_expr(p, e->fn = parser_calloc(p, 1, sizeof *e->fn))) return false; e->fn->export.id = 0; if (t->token != end) { @@ -1831,9 +1831,30 @@ static bool parse_decl(Parser *p, Declaration *d, DeclEndKind ends_with, U16 fla if (token_is_kw(t->token, KW_EQ)) { ++t->token; if (token_is_direct(t->token, DIRECT_FOREIGN)) { + if (!(d->flags & DECL_ANNOTATES_TYPE)) { + err_print(d->where, "Foreign declaration must have a type."); + return false; + } d->flags |= DECL_FOREIGN; + /* foreign name */ ++t->token; - /* TODO: foreign name */ + d->foreign.name = parser_new_expr(p); + if (!parse_expr(p, d->foreign.name, expr_find_end(p, EXPR_CAN_END_WITH_COMMA))) { + goto ret_false; + } + if (!ends_decl(t->token, ends_with)) { + if (!token_is_kw(t->token, KW_COMMA)) { + tokr_err(t, "Expected comma, followed by foreign library."); + goto ret_false; + } + ++t->token; + /* foreign library */ + d->foreign.lib = parser_new_expr(p); + if (!parse_expr(p, d->foreign.lib, expr_find_end(p, 0))) { + goto ret_false; + } + } + if (!ends_decl(t->token, ends_with)) { tokr_err(t, "Expected declaration to stop after #foreign, but it continues."); goto ret_false; diff --git a/sdecls_cgen.c b/sdecls_cgen.c index 1aa8e51..2bd71a0 100644 --- a/sdecls_cgen.c +++ b/sdecls_cgen.c @@ -70,6 +70,10 @@ static bool cgen_sdecls_expr(CGenerator *g, Expression *e) { } static bool cgen_sdecls_decl(CGenerator *g, Declaration *d) { + if (d->flags & DECL_FOREIGN) { + /* handled by cgen_decls */ + return true; + } cgen_sdecls_type(g, &d->type); if (cgen_fn_is_direct(g, d)) { d->expr.fn->c.name = d->idents[0]; diff --git a/test.toc b/test.toc index aa12b62..d4072af 100644 --- a/test.toc +++ b/test.toc @@ -5,11 +5,17 @@ import ::= fn(x :: []char) &Package { p_ptr }; -// n should be size_t -memcpy :: fn (&u8, &u8, u64) = #foreign; +puts :: fn(&char) i32 = #foreign "puts", "libc.so.6"; +hw ::= fn() int { + s := "Hello, world!\0"; + puts(&s[0]); + 0 +}; main ::= fn() { - io ::= import("std/io"); - io.puts("Hello, world!"); + // io ::= import("std/io"); + // io.puts("Hello, world!"); + hw(); + x ::= hw(); }; \ No newline at end of file diff --git a/tokenizer.c b/tokenizer.c index 1ca3b3b..8c26769 100644 --- a/tokenizer.c +++ b/tokenizer.c @@ -130,7 +130,19 @@ static inline void tokr_nextchar(Tokenizer *t) { ++t->s; } -static char tokr_esc_seq(Tokenizer *t) { +/* returns -1 if not a hex digit, otherwise 0-15 */ +static inline int char_as_hex_digit(char c) { + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; + return -1; +} + +/* returns -1 if escape sequence is invalid */ +static int tokr_esc_seq(Tokenizer *t) { /* TODO: add more of these incl. \x41, \100 */ switch (*t->s) { case '\'': @@ -145,8 +157,21 @@ static char tokr_esc_seq(Tokenizer *t) { case 'n': tokr_nextchar(t); return '\n'; + case '0': + tokr_nextchar(t); + return '\0'; + case 'x': { + int c1 = char_as_hex_digit(t->s[1]); + if (c1 == -1) return 0; + int c2 = char_as_hex_digit(t->s[2]); + if (c2 == -1) return 0; + tokr_nextchar(t); + tokr_nextchar(t); + tokr_nextchar(t); + return (char)(c1 * 16 + c2); + } default: - return 0; + return -1; } } @@ -159,8 +184,16 @@ static Location token_location(Token *t) { } /* for use during tokenization */ -static void tokenization_err(Tokenizer *t, const char *fmt, ...) { +static void tokenization_err_( +#if ERR_SHOW_SOURCE_LOCATION + const char *src_file, int src_line, +#endif + Tokenizer *t, const char *fmt, ...) { va_list args; + if (!t->err_ctx->enabled) return; +#if ERR_SHOW_SOURCE_LOCATION + err_fprint("Generated by line %d of %s:\n", src_line, src_file); +#endif va_start(args, fmt); err_text_err(t->err_ctx, "error"); err_fprint(" at line %lu of %s:\n", (unsigned long)t->line, t->err_ctx->filename); @@ -178,15 +211,21 @@ static void tokenization_err(Tokenizer *t, const char *fmt, ...) { } } +#if ERR_SHOW_SOURCE_LOCATION +#define tokenization_err(...) tokenization_err_(__FILE__, __LINE__, __VA_ARGS__) +#else +#define tokenization_err tokenization_err_ +#endif + /* for use after tokenization */ static void tokr_err_( #if ERR_SHOW_SOURCE_LOCATION const char *src_file, int src_line, #endif Tokenizer *t, const char *fmt, ...) { + if (!t->err_ctx->enabled) return; #if ERR_SHOW_SOURCE_LOCATION - if (!t->token->pos.ctx->enabled) return; - err_fprint("At line %d of %s:\n", src_line, src_file); + err_fprint("Generated by line %d of %s:\n", src_line, src_file); #endif va_list args; va_start(args, fmt); @@ -454,11 +493,12 @@ static bool tokenize_string(Tokenizer *t, char *str) { if (*t->s == '\\') { /* escape sequence */ tokr_nextchar(t); - c = tokr_esc_seq(t); - if (c == 0) { + int e = tokr_esc_seq(t); + if (e == -1) { tokenization_err(t, "Unrecognized escape character: '\\%c'.", *t->s); goto err; } + c = (char)e; } else { c = *t->s; tokr_nextchar(t); @@ -502,12 +542,12 @@ static bool tokenize_string(Tokenizer *t, char *str) { assert(*t->s); if (*t->s == '\\') { tokr_nextchar(t); - char c = tokr_esc_seq(t); - if (c == 0) { + int c = tokr_esc_seq(t); + if (c == -1) { tokenization_err(t, "Unrecognized escape character: '\\%c'.", *t->s); goto err; } - *strptr++ = c; + *strptr++ = (char)c; } else { *strptr++ = *t->s; tokr_nextchar(t); diff --git a/types.c b/types.c index 11c40a4..f2208be 100644 --- a/types.c +++ b/types.c @@ -842,6 +842,21 @@ static bool call_arg_param_order(Allocator *allocr, FnExpr *fn, Type *fn_type, A nparams, nargs); return false; } + + U16 *order = NULL; + if (fn->flags & FN_EXPR_FOREIGN) { + U16 i = 0; + arr_foreach(args, Argument, arg) { + if (arg->name) { + err_print(arg->where, "Foreign function calls cannot use named arguments."); + return false; + } + *(U16 *)arr_adda(&order, allocr) = i++; + } + *param_indices = order; + return true; + } + int p = 0; /* counter for sequential parameters */ Declaration *last_param_without_default_value = NULL; @@ -852,7 +867,6 @@ static bool call_arg_param_order(Allocator *allocr, FnExpr *fn, Type *fn_type, A } Declaration *param = fn->params; size_t ident_idx = 0; - U16 *order = NULL; arr_foreach(args, Argument, arg) { bool named = arg->name != NULL; int param_idx = -1; @@ -2121,6 +2135,26 @@ static bool types_block(Typer *tr, Block *b) { return success; } +/* returns NULL if an error occured */ +static char *eval_expr_as_cstr(Typer *tr, Expression *e, const char *what_is_this) { + Value e_val; + if (!types_expr(tr, e)) + return NULL; + if (!type_is_slicechar(&e->type)) { + char *got = type_to_str(&e->type); + err_print(e->where, "Expected []char for %s, but got %s.", what_is_this, got); + free(got); + return NULL; + } + if (!eval_expr(tr->evalr, e, &e_val)) + return NULL; + Slice e_slice = e_val.slice; + char *str = typer_malloc(tr, (size_t)e_slice.n + 1); + str[e_slice.n] = 0; + memcpy(str, e_slice.data, (size_t)e_slice.n); + return str; +} + static bool types_decl(Typer *tr, Declaration *d) { bool success = true; if (d->flags & DECL_FOUND_TYPE) return true; @@ -2179,7 +2213,35 @@ static bool types_decl(Typer *tr, Declaration *d) { } } + } else if (d->flags & DECL_FOREIGN) { + if (!type_resolve(tr, &d->type, d->where)) { + success = false; + goto ret; + } + char *name_cstr = eval_expr_as_cstr(tr, d->foreign.name, "foreign name"); + if (!name_cstr) { + success = false; + goto ret; + } + if (d->foreign.lib) { + char *lib_cstr = eval_expr_as_cstr(tr, d->foreign.lib, "foreign library name"); + if (!lib_cstr) { + success = false; + goto ret; + } + /* make sure no one tries to use these */ + d->foreign.name = NULL; + d->foreign.lib = NULL; + FnExpr *f = d->val.fn = typer_calloc(tr, 1, sizeof *d->expr.fn); + f->flags = FN_EXPR_FOREIGN; + f->where = d->expr.where = d->where; + f->foreign.name = name_cstr; + f->foreign.lib = lib_cstr; + + d->flags |= DECL_FOUND_VAL; + } } + for (size_t i = 0; i < arr_len(d->idents); ++i) { Type *t = d->type.kind == TYPE_TUPLE ? &d->type.tuple[i] : &d->type; if (type_is_compileonly(&d->type)) { diff --git a/types.h b/types.h index f3adcee..7ba2f18 100644 --- a/types.h +++ b/types.h @@ -571,12 +571,22 @@ typedef struct EachExpr { } EachExpr; +enum { + FN_EXPR_FOREIGN = 0x01 +}; + typedef struct FnExpr { struct Declaration *params; /* declarations of the parameters to this function */ struct Declaration *ret_decls; /* array of decls, if this has named return values. otherwise, NULL */ Location where; - Type ret_type; - Block body; + Type ret_type; + union { + Block body; + struct { + const char *name; + const char *lib; + } foreign; + }; HashTable instances; /* for fns with constant parameters. the key is a tuple where the first element is a u64 value whose ith bit (1<