summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cgen.c8
-rw-r--r--decls_cgen.c4
-rw-r--r--eval.c9
-rw-r--r--main.c2
-rw-r--r--package.c87
-rw-r--r--parse.c33
-rw-r--r--sdecls_cgen.c4
-rw-r--r--test.toc14
-rw-r--r--tokenizer.c60
-rw-r--r--types.c64
-rw-r--r--types.h24
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<<i) is 1
if the ith semi-constant parameter is constant.
@@ -594,6 +604,7 @@ typedef struct FnExpr {
bool declared;
bool defined;
} c;
+ U8 flags;
} FnExpr; /* an expression such as fn(x: int) int { 2 * x } */
typedef struct Instance {
@@ -709,7 +720,14 @@ typedef struct Declaration {
Identifier *idents;
Type type;
DeclFlags flags;
- Expression expr;
+ union {
+ Expression expr;
+ struct {
+ /* only exist before typing */
+ Expression *name;
+ Expression *lib;
+ } foreign;
+ };
Value val; /* only for constant decls. */
} Declaration;