Skip to content

Commit

Permalink
support type annotations in script
Browse files Browse the repository at this point in the history
  • Loading branch information
annacrombie committed Dec 12, 2023
1 parent 5f25b3d commit de372ba
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 114 deletions.
3 changes: 3 additions & 0 deletions include/lang/lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ enum token_type {
tok_func,
tok_endfunc,
tok_return,
tok_bitor,
tok_returntype,

/* formatting only */
tok_comment,
Expand All @@ -81,6 +83,7 @@ enum token_type {
union token_data {
const char *s;
int64_t n;
uint64_t type;
};

struct token {
Expand Down
2 changes: 2 additions & 0 deletions include/lang/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ struct obj_func {
enum language_mode lang_mode;
uint32_t args_id, block_id, nargs, nkwargs;
obj kwarg_defaults, src, scope_stack;
type_tag return_type;
};

enum tgt_type {
Expand Down Expand Up @@ -597,6 +598,7 @@ OBJ_GETTER(obj_source_configuration);
struct sbuf;

const char *obj_type_to_s(enum obj_type t);
bool s_to_type_tag(const char *s, type_tag *t);
void obj_to_s(struct workspace *wk, obj o, struct sbuf *sb);
bool obj_equal(struct workspace *wk, obj left, obj right);
bool obj_clone(struct workspace *wk_src, struct workspace *wk_dest, obj val, obj *ret);
Expand Down
18 changes: 13 additions & 5 deletions src/functions/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ typecheck_function_arg(struct workspace *wk, uint32_t err_node, obj *val, type_t

assert((type & obj_typechecking_type_tag) || type < obj_type_count);

// If obj_file or tc_file is requested, and the arugment is an array of
// If obj_file or tc_file is requested, and the argument is an array of
// length 1, try to unpack it.
if (!array_of && (type == obj_file || (type & tc_file) == tc_file)) {
if (get_obj_type(wk, *val) == obj_array
Expand Down Expand Up @@ -725,17 +725,21 @@ func_obj_eval(struct workspace *wk, obj func_obj, obj func_module, uint32_t args
memset(akw, 0, sizeof(struct args_kw) * (f->nkwargs + 1));
akw[f->nkwargs].type = ARG_TYPE_NULL;

uint32_t i = 0, arg_id = f->args_id;
uint32_t pos_i = 0, kw_i = 0, arg_id = f->args_id;
while (true) {
struct node *arg = arr_get(&f->ast->nodes, arg_id);
if (arg->type == node_empty) {
break;
}

if (arg->subtype == arg_kwarg) {
if (arg->subtype == arg_normal) {
an[pos_i].type = arg->dat.type;
++pos_i;
} else if (arg->subtype == arg_kwarg) {
struct node *key = arr_get(&f->ast->nodes, arg->l);
akw[i].key = key->dat.s;
++i;
akw[kw_i].key = key->dat.s;
akw[kw_i].type = arg->dat.type;
++kw_i;
}

if (!(arg->chflg & node_child_c)) {
Expand Down Expand Up @@ -799,6 +803,10 @@ func_obj_eval(struct workspace *wk, obj func_obj, obj func_module, uint32_t args
wk->returned = 0;
ret = wk->interp_node(wk, f->block_id, &_);
*res = wk->returned;
if (ret && !typecheck(wk, args_node, *res, f->return_type)) {
interp_error(wk, args_node, "function returned invalid type");
ret = false;
}
wk->returning = false;

ret:
Expand Down
45 changes: 40 additions & 5 deletions src/lang/fmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@
#include "formats/editorconfig.h"
#include "formats/ini.h"
#include "lang/fmt.h"
#include "lang/interpreter.h"
#include "lang/string.h"
#include "log.h"
#include "platform/mem.h"
#include "platform/path.h"

struct arg_elem {
uint32_t kw, val, next;
type_tag type;
};

struct fmt_ctx {
struct ast *ast;
struct workspace *wk;
struct sbuf *out_buf;
uint32_t indent, col, enclosed;
bool force_ml;
Expand Down Expand Up @@ -408,6 +411,8 @@ fmt_args(struct fmt_ctx *ctx, const struct fmt_stack *pfst, uint32_t n_args)
ae->val = arg->l;
}

ae->type = arg->dat.type;

arg_val = get_node(ctx->ast, ae->val);

{ // deal with empty lines and comment lines
Expand Down Expand Up @@ -450,8 +455,19 @@ fmt_args(struct fmt_ctx *ctx, const struct fmt_stack *pfst, uint32_t n_args)
fst.special_fmt |= special_fmt_cmd_array;
}

fst.node_sep = ctx->wide_colon ? " : " : ": ";
len += fmt_node(ctx, &fst, ae->kw);
const char *kw_sep = ctx->wide_colon ? " : " : ": ";

if (ae->type) {
fst.node_sep = 0;
len += fmt_node(ctx, &fst, ae->kw);
len += fmt_writes(ctx, &fst, " ");
len += fmt_writes(ctx, &fst, typechecking_type_to_s(ctx->wk, ae->type));
fst.node_sep = kw_sep;
len += fmt_tail(ctx, &fst, ae->kw);
} else {
fst.node_sep = kw_sep;
len += fmt_node(ctx, &fst, ae->kw);
}
}

bool need_comma;
Expand All @@ -469,8 +485,18 @@ fmt_args(struct fmt_ctx *ctx, const struct fmt_stack *pfst, uint32_t n_args)
need_comma = true;
}

fst.node_sep = need_comma ? "," : NULL;
len += fmt_check(ctx, &fst, fmt_node, ae->val);
const char *val_sep = need_comma ? "," : NULL;
if (!ae->kw && ae->type) {
fst.node_sep = 0;
len += fmt_node(ctx, &fst, ae->val);
len += fmt_writes(ctx, &fst, " ");
len += fmt_writes(ctx, &fst, typechecking_type_to_s(ctx->wk, ae->type));
fst.node_sep = val_sep;
len += fmt_tail(ctx, &fst, ae->val);
} else {
fst.node_sep = val_sep;
len += fmt_check(ctx, &fst, fmt_node, ae->val);
}

if (!last) {
if ((pfst->special_fmt & special_fmt_cmd_array)
Expand Down Expand Up @@ -849,7 +875,12 @@ fmt_node(struct fmt_ctx *ctx, const struct fmt_stack *pfst, uint32_t n_id)
struct fmt_stack arg_fst = *fmt_setup_fst(&fst);
arg_fst.arg_container = "()";
arg_fst.ml = false;
len += fmt_check(ctx, &arg_fst, fmt_arg_container, n->r);//(ctx, &arg_fst, n->r);
len += fmt_check(ctx, &arg_fst, fmt_arg_container, n->r);

if (n->dat.type) {
len += fmt_writes(ctx, &fst, " -> ");
len += fmt_writes(ctx, &fst, typechecking_type_to_s(ctx->wk, n->dat.type));
}

struct node *block = get_node(ctx->ast, n->c);
if (block->type == node_empty) {
Expand Down Expand Up @@ -1063,8 +1094,11 @@ fmt(struct source *src, FILE *out, const char *cfg_path, bool check_only, bool e
struct ast ast = { 0 };
struct source_data sdata = { 0 };
struct sbuf out_buf;
struct workspace wk = { 0 };
workspace_init_bare(&wk);
struct fmt_ctx ctx = {
.ast = &ast,
.wk = &wk,
.out_buf = &out_buf,
.max_line_len = 80,
.indent_by = " ",
Expand Down Expand Up @@ -1125,6 +1159,7 @@ fmt(struct source *src, FILE *out, const char *cfg_path, bool check_only, bool e

ret = true;
ret:
workspace_destroy_bare(&wk);
if (cfg_buf) {
z_free(cfg_buf);
}
Expand Down
19 changes: 18 additions & 1 deletion src/lang/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,32 @@ typechecking_type_to_arr(struct workspace *wk, type_tag t)
obj expected_types;
make_obj(wk, &expected_types, obj_array);

if ((t & ARG_TYPE_GLOB)) {
t &= ~ARG_TYPE_GLOB;
obj_array_push(wk, expected_types, make_str(wk, "glob"));
}
if ((t & ARG_TYPE_ARRAY_OF)) {
t &= ~ARG_TYPE_ARRAY_OF;
obj_array_push(wk, expected_types, make_str(wk, "listify"));
}

const char *single = NULL;
if (!(t & obj_typechecking_type_tag)) {
single = obj_type_to_s(t);
} else if (t == tc_any) {
single = "any";
} else if (t == tc_exe) {
single = "exe";
} else if (t == obj_typechecking_type_tag) {
single = "null";
single = "void";
}

if (single) {
if (get_obj_array(wk,expected_types)->len && strcmp(single, "void") == 0) {
// avoids outputting `listify|void` when `listify` has the same meaning
return expected_types;
}

obj_array_push(wk, expected_types, make_str(wk, single));
return expected_types;
}
Expand Down Expand Up @@ -1296,6 +1312,7 @@ interp_func_def(struct workspace *wk, struct node *n)
f->ast = wk->ast;
f->lang_mode = wk->lang_mode;
f->scope_stack = wk->scope_stack_dup(wk, current_project(wk)->scope_stack);
f->return_type = n->dat.type;

struct node *arg;
uint32_t arg_id = n->r;
Expand Down
17 changes: 16 additions & 1 deletion src/lang/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ tok_type_to_s(enum token_type type)
case tok_func: return "func";
case tok_endfunc: return "endfunc";
case tok_return: return "return";
case tok_bitor: return "|";
case tok_returntype: return "->";
case tok_comment: return "comment";
case tok_fmt_eol: return "fmt_eol";
}
Expand Down Expand Up @@ -805,7 +807,12 @@ lexer_tokenize_one(struct lexer *lexer)
}
break;
case '-':
token->type = tok_minus;
if ((lexer->mode & lexer_mode_functions) && lexer->src[lexer->i + 1] == '>') {
advance(lexer);
token->type = tok_returntype;
} else {
token->type = tok_minus;
}
break;
case '*':
token->type = tok_star;
Expand Down Expand Up @@ -856,6 +863,14 @@ lexer_tokenize_one(struct lexer *lexer)
}
token->type = tok_eof;
return lex_done;
case '|':
if (lexer->mode & lexer_mode_functions) {
token->type = tok_bitor;
} else {
lex_error(lexer, "unexpected character: '%c'", lexer->src[lexer->i]);
return lex_fail;
}
break;
default:
lex_error(lexer, "unexpected character: '%c'", lexer->src[lexer->i]);
return lex_fail;
Expand Down
111 changes: 73 additions & 38 deletions src/lang/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,53 +319,88 @@ obj_clear(struct workspace *wk, const struct obj_clear_mark *mk)
}
}

static struct
{
enum obj_type t;
const char *name;
} obj_names[obj_type_count] = {
{ .t = obj_null, .name = "void" },
{ .t = obj_compiler, .name = "compiler" },
{ .t = obj_dependency, .name = "dep" },
{ .t = obj_meson, .name = "meson" },
{ .t = obj_string, .name = "str" },
{ .t = obj_number, .name = "int" },
{ .t = obj_array, .name = "list" },
{ .t = obj_dict, .name = "dict" },
{ .t = obj_bool, .name = "bool" },
{ .t = obj_file, .name = "file" },
{ .t = obj_build_target, .name = "build_tgt" },
{ .t = obj_subproject, .name = "subproject" },
{ .t = obj_machine, .name = "build_machine" },
{ .t = obj_feature_opt, .name = "feature" },
{ .t = obj_external_program, .name = "external_program" },
{ .t = obj_python_installation, .name = "python_installation" },
{ .t = obj_run_result, .name = "runresult" },
{ .t = obj_configuration_data, .name = "cfg_data" },
{ .t = obj_custom_target, .name = "custom_tgt" },
{ .t = obj_test, .name = "test" },
{ .t = obj_module, .name = "module" },
{ .t = obj_install_target, .name = "install_tgt" },
{ .t = obj_environment, .name = "env" },
{ .t = obj_include_directory, .name = "inc" },
{ .t = obj_option, .name = "option" },
{ .t = obj_disabler, .name = "disabler" },
{ .t = obj_generator, .name = "generator" },
{ .t = obj_generated_list, .name = "generated_list" },
{ .t = obj_alias_target, .name = "alias_tgt" },
{ .t = obj_both_libs, .name = "both_libs" },
{ .t = obj_typeinfo, .name = "typeinfo" },
{ .t = obj_func, .name = "function" },
{ .t = obj_source_set, .name = "source_set" },
{ .t = obj_source_configuration, .name = "source_configuration" },
};

const char *
obj_type_to_s(enum obj_type t)
{
switch (t) {
case obj_null: return "void";
case obj_compiler: return "compiler";
case obj_dependency: return "dep";
case obj_meson: return "meson";
case obj_string: return "str";
case obj_number: return "int";
case obj_array: return "list";
case obj_dict: return "dict";
case obj_bool: return "bool";
case obj_file: return "file";
case obj_build_target: return "build_tgt";
case obj_subproject: return "subproject";
case obj_machine: return "build_machine";
case obj_feature_opt: return "feature";
case obj_external_program: return "external_program";
case obj_python_installation: return "python_installation";
case obj_run_result: return "runresult";
case obj_configuration_data: return "cfg_data";
case obj_custom_target: return "custom_tgt";
case obj_test: return "test";
case obj_module: return "module";
case obj_install_target: return "install_tgt";
case obj_environment: return "env";
case obj_include_directory: return "inc";
case obj_option: return "option";
case obj_disabler: return "disabler";
case obj_generator: return "generator";
case obj_generated_list: return "generated_list";
case obj_alias_target: return "alias_tgt";
case obj_both_libs: return "both_libs";
case obj_typeinfo: return "typeinfo";
case obj_func: return "func";
case obj_source_set: return "source_set";
case obj_source_configuration: return "source_configuration";

case obj_type_count:
assert(false); return "uh oh";
uint32_t i;
for (i = 0; i < obj_type_count; ++i) {
if (obj_names[i].t == t) {
return obj_names[i].name;
}
}

assert(false && "unreachable");
return NULL;
}

bool
s_to_type_tag(const char *s, type_tag *t)
{
uint32_t i;
for (i = 0; i < obj_type_count; ++i) {
if (strcmp(s, obj_names[i].name) == 0) {
*t = obj_type_to_tc_type(obj_names[i].t);
return true;
}
}

struct { type_tag t; const char *name; } extra_types[] = {
{ .t = tc_exe, .name = "exe" },
{ .t = ARG_TYPE_ARRAY_OF, .name = "listify" },
{ .t = ARG_TYPE_GLOB, .name = "glob" },
};

for (i = 0; i < ARRAY_LEN(extra_types); ++i) {
if (strcmp(s, extra_types[i].name) == 0) {
*t = extra_types[i].t;
return true;
}
}

return false;
}

struct obj_equal_iter_ctx {
obj other_container;
uint32_t i;
Expand Down
Loading

0 comments on commit de372ba

Please sign in to comment.