~jack/misc

56bcbb628c437030a4c845850deb2dd1fc34e9fa — Jack Kelly 6 months ago 68a0c1c
lambda-c: parse STLC typedecls in lambdas
M lambda-c/README.md => lambda-c/README.md +3 -1
@@ 1,6 1,8 @@
# lambda-c

Playing around with lambda calculus interpreters in C99.
Playing around with lambda calculus interpreters in C99. Parses STLC
lambdas, but doesn't typecheck or anything. Probably won't push it
further.

When finished, it should compile cleanly under `./configure
CFLAGS='-std=c99 -pedantic -Wall -Wextra'`.

M lambda-c/src/Makefile.am => lambda-c/src/Makefile.am +2 -1
@@ 9,7 9,8 @@ lambda_SOURCES = lambda.c \
	eval.c eval.h \
	parser.c parser.h \
	refcount.c refcount.h \
	term.c term.h
	term.c term.h \
	type.c type.h

EXTRA_DIST = lexer.rl
CLEANFILES = lexer.c

M lambda-c/src/eval.c => lambda-c/src/eval.c +13 -10
@@ 47,8 47,8 @@ free_vars(const Term *t) {
    g_hash_table_remove(r, t->lam.v);
    return r;
  }
  default: g_assert_not_reached();
  }
  g_assert_not_reached();
}

static Term*


@@ 61,7 61,7 @@ sub(Term *t, const gchar *from, Term *to, gint *gensym_count) {
                    sub(t->app.t2, from, to, gensym_count));
  case TERM_LAM:
    if (strcmp(t->lam.v, from) == 0) {
      return term_ref(t);
      return term_ref_inc(t);
    } else {
      g_autoptr(GHashTable) fvs = free_vars(to);
      if (g_hash_table_contains(fvs, t->lam.v)) {


@@ 69,13 69,17 @@ sub(Term *t, const gchar *from, Term *to, gint *gensym_count) {
        g_autoptr(Term) new_var = term_var(gensym);
        g_autoptr(Term) renamed_body =
          sub(t->lam.t, t->lam.v, new_var, gensym_count);
        return term_lam(gensym, sub(renamed_body, from, to, gensym_count));
        return term_lam(gensym,
                        t->lam.ty,
                        sub(renamed_body, from, to, gensym_count));
      } else {
        return term_lam(t->lam.v, sub(t->lam.t, from, to, gensym_count));
        return term_lam(t->lam.v,
                        t->lam.ty,
                        sub(t->lam.t, from, to, gensym_count));
      }
    }
  default: g_assert_not_reached();
  }
  g_assert_not_reached();
}

static Term*


@@ 83,12 87,11 @@ apply(Term *t1, Term *t2, gint *gensym_count) {
  switch (t1->tag) {
  case TERM_VAR:
  case TERM_APP:
    return term_app(term_ref(t1), term_ref(t2));
    return term_app(t1, t2);
  case TERM_LAM:
    return sub(t1->lam.t, t1->lam.v, t2, gensym_count);
  default:
    g_assert_not_reached();
  }
  g_assert_not_reached();
}

static Term*


@@ 96,14 99,14 @@ eval_rec(Term *t, gint *gensym_count) {
  switch (t->tag) {
  case TERM_VAR:
  case TERM_LAM:
    return term_ref(t);
    return term_ref_inc(t);
  case TERM_APP: {
    g_autoptr(Term) t1e = eval_rec(t->app.t1, gensym_count);
    g_autoptr(Term) t2e = eval_rec(t->app.t2, gensym_count);
    return apply(t1e, t2e, gensym_count);
  }
  default: g_assert_not_reached();
  }
  g_assert_not_reached();
}

Term*

M lambda-c/src/lambda.c => lambda-c/src/lambda.c +14 -4
@@ 27,10 27,20 @@

#include "lexer.h"
#include "parser.h"
#include "type.h"

int main(int argc, char *argv[]) {
  g_autoptr(GArray) tokens = lex("((\\w.\\x.\\y.\\z.(x)(w q))(y))(z)");
  Term *t = parse(tokens);
  g_autoptr(GArray) tokens = lex("(\\(x : Int -> Bool) . x y) even");
  for (guint i = 0; i < tokens->len; i++) {
    struct token *tok = &g_array_index(tokens, struct token, i);
    token_puts(tok);
  }
  Term *t = parse_result_unwrap_term(parse(tokens));

  struct type *ty = type_arr(type_arr(type_constant("Int"), type_constant("Bool")), type_constant("String"));
  type_fput(ty, stdout);
  puts("");
  type_ref_dec(ty);

  term_fput(t, stdout);
  puts("");


@@ 39,7 49,7 @@ int main(int argc, char *argv[]) {
  term_fput(e, stdout);
  puts("");

  term_unref(e);
  term_unref(t);
  term_ref_dec(e);
  term_ref_dec(t);
  return 0;
}

M lambda-c/src/lexer.h => lambda-c/src/lexer.h +8 -1
@@ 7,9 7,12 @@ enum token_type {
  TOKEN_EOF,
  TOKEN_LAMBDA,
  TOKEN_DOT,
  TOKEN_COLON,
  TOKEN_ARR,
  TOKEN_LPAREN,
  TOKEN_RPAREN,
  TOKEN_VAR
  TOKEN_VAR,
  TOKEN_CONSTANT
};

struct token {


@@ 20,9 23,13 @@ struct token {
void token_eof(struct token *t);
void token_lambda(struct token *t);
void token_dot(struct token *t);
void token_colon(struct token *t);
void token_arr(struct token *t);
void token_lparen(struct token *t);
void token_rparen(struct token *t);
void token_var(struct token *t, gchar *v); /* takes ownership of v */
void token_constant(struct token *t, gchar *v); /* takes ownership of v */
void token_copy(struct token *dest, const struct token *src);
void token_clear(struct token *t);
void token_puts(const struct token *t);


M lambda-c/src/lexer.rl => lambda-c/src/lexer.rl +48 -3
@@ 18,6 18,14 @@ main := |*
    token_dot(&t);
    g_array_append_val(r, t);
  };
  ':' => {
    token_colon(&t);
    g_array_append_val(r, t);
  };
  '->' => {
    token_arr(&t);
    g_array_append_val(r, t);
  };
  '(' => {
    token_lparen(&t);
    g_array_append_val(r, t);


@@ 26,10 34,14 @@ main := |*
    token_rparen(&t);
    g_array_append_val(r, t);
  };
  alpha+ => {
  lower . alpha* => {
    token_var(&t, g_strndup(ts, te - ts));
    g_array_append_val(r, t);
  };
  upper . alpha* => {
    token_constant(&t, g_strndup(ts, te - ts));
    g_array_append_val(r, t);
  };
  space;
*|;



@@ 56,6 68,16 @@ token_dot(struct token *t) {
}

void
token_colon(struct token *t) {
  t->type = TOKEN_COLON;
}

void
token_arr(struct token *t) {
  t->type = TOKEN_ARR;
}

void
token_lparen(struct token *t) {
  t->type = TOKEN_LPAREN;
}


@@ 72,8 94,22 @@ token_var(struct token *t, gchar *v) {
}

void
token_constant(struct token *t, gchar *v) {
  t->type = TOKEN_CONSTANT;
  t->v = v;
}

void
token_copy(struct token *dest, const struct token *src) {
  dest->type = src->type;
  if (src->type == TOKEN_VAR || src->type == TOKEN_CONSTANT) {
    dest->v = g_strdup(src->v);
  }
}

void
token_clear(struct token *t) {
  if (t->type == TOKEN_VAR) g_free(t->v);
  if (t->type == TOKEN_VAR || t->type == TOKEN_CONSTANT) g_free(t->v);
}

void


@@ 88,6 124,12 @@ token_puts(const struct token *t) {
  case TOKEN_DOT:
    printf("Dot\n");
    return;
  case TOKEN_COLON:
    printf("Colon\n");
    return;
  case TOKEN_ARR:
    printf("Arr\n");
    return;
  case TOKEN_LPAREN:
    printf("Lparen\n");
    return;


@@ 97,8 139,11 @@ token_puts(const struct token *t) {
  case TOKEN_VAR:
    printf("Var(%s)\n", t->v);
    return;
  default: g_assert_not_reached();
  case TOKEN_CONSTANT:
    printf("Constant(%s)\n", t->v);
    return;
  }
  g_assert_not_reached();
}

static void

M lambda-c/src/parser.c => lambda-c/src/parser.c +224 -23
@@ 1,7 1,76 @@
#include "config.h"
#include "parser.h"

#include "lexer.h"
#include "type.h"

struct parse_result*
parse_result_term(Term *t) {
  struct parse_result *r = g_new(struct parse_result, 1);
  r->tag = PARSE_RESULT_TERM;
  r->term = term_ref_sink(t);
  return r;
}

struct parse_result*
parse_result_type(struct type *ty) {
  struct parse_result *r = g_new(struct parse_result, 1);
  r->tag = PARSE_RESULT_TYPE;
  r->type = type_ref_sink(ty);
  return r;
}

struct parse_result*
parse_result_error_expected(enum token_type expected,
                            const struct token *got) {
  struct parse_result *r = g_new(struct parse_result, 1);
  r->tag = PARSE_RESULT_ERROR_EXPECTED;
  r->expected.expected = expected;
  token_copy(&r->expected.got, got);
  return r;
}

Term*
parse_result_unwrap_term(struct parse_result *p) {
  if (p->tag != PARSE_RESULT_TERM) {
    fprintf(stderr, "parse_result_unwrap_term: not term\n");
    abort();
  }

  Term *t = term_ref_inc(p->term);
  parse_result_free(p);
  term_ref_float(t);
  return t;
}

struct type*
parse_result_unwrap_type(struct parse_result *p) {
  if (p->tag != PARSE_RESULT_TYPE) {
    fprintf(stderr, "parse_result_unwrap_type: not type\n");
    abort();
  }

  struct type *ty = type_ref_inc(p->type);
  parse_result_free(p);
  type_ref_float(ty);
  return ty;
}

void
parse_result_free(struct parse_result *p) {
  switch (p->tag) {
  case PARSE_RESULT_TERM:
    term_ref_dec(p->term);
    break;
  case PARSE_RESULT_TYPE:
    type_ref_dec(p->type);
    break;
  case PARSE_RESULT_ERROR_EXPECTED:
    token_clear(&p->expected.got);
    break;
  }

  g_free(p);
}

struct parser_state {
  const GArray *tokens;


@@ 14,32 83,143 @@ peek_token(struct parser_state *st) {
  return &g_array_index(st->tokens, const struct token, st->token_idx);
}

static const struct token*
expect_token(struct parser_state *st, enum token_type expected) {
  const struct token *r = peek_token(st);
static int
expect_token(struct parser_state *st,
             enum token_type expected,
             const struct token **t) {
  *t = peek_token(st);
  st->token_idx++;
  if (r->type != expected) abort();
  return r;
  return (*t)->type == expected;
}

static Term* parse_rec(struct parser_state *st);
static struct parse_result* parse_rec(struct parser_state *st);

static Term* parse_var(struct parser_state *st) {
  const struct token *t = expect_token(st, TOKEN_VAR);
  return term_var(t->v);
static struct parse_result*
parse_var(struct parser_state *st) {
  const struct token *t;
  if (expect_token(st, TOKEN_VAR, &t)) {
    return parse_result_term(term_var(t->v));
  } else {
    return parse_result_error_expected(TOKEN_VAR, t);
  }
}

static Term* parse_lam(struct parser_state *st) {
  expect_token(st, TOKEN_LAMBDA);
  const struct token *v = expect_token(st, TOKEN_VAR);
  expect_token(st, TOKEN_DOT);
  Term *t = parse_rec(st);
  return term_lam(v->v, t);
static struct parse_result*
parse_type_constant(struct parser_state *st) {
  const struct token *t;
  if (expect_token(st, TOKEN_CONSTANT, &t)) {
    return parse_result_type(type_constant(t->v));
  } else {
    return parse_result_error_expected(TOKEN_CONSTANT, t);
  }
}

static Term* parse_rec(struct parser_state *st) {
static struct parse_result*
parse_type(struct parser_state *st) {
  const struct token *t = peek_token(st);
  Term *r;
  struct parse_result *r = NULL;

  switch (t->type) {
  case TOKEN_CONSTANT:
    r = parse_type_constant(st);
    break;
  case TOKEN_LPAREN: {
    const struct token *lpar;
    if (!expect_token(st, TOKEN_LPAREN, &lpar)) {
      return parse_result_error_expected(TOKEN_LPAREN, lpar);
    }

    r = parse_rec(st);
    if (r->tag != PARSE_RESULT_TERM) return r;

    const struct token *rpar;
    if (!expect_token(st, TOKEN_RPAREN, &rpar)) {
      return parse_result_error_expected(TOKEN_RPAREN, rpar);
    }
    break;
  }
  case TOKEN_EOF:
  case TOKEN_LAMBDA:
  case TOKEN_DOT:
  case TOKEN_COLON:
  case TOKEN_ARR:
  case TOKEN_RPAREN:
  case TOKEN_VAR:
    printf("parse_type: unexpected token: ");
    token_puts(t);
    abort();
  }

  g_assert(r != NULL);
  if (r->tag != PARSE_RESULT_TYPE) return r;

  t = peek_token(st);
  if (t->type == TOKEN_RPAREN) return r;

  const struct token *arr;
  if (!expect_token(st, TOKEN_ARR, &arr)) {
    parse_result_free(r);
    return parse_result_error_expected(TOKEN_ARR, arr);
  }

  struct type *ty = parse_result_unwrap_type(r);
  r = parse_type(st);
  if (r->tag != PARSE_RESULT_TYPE) {
    type_ref_dec(ty);
    return r;
  }

  return parse_result_type(type_arr(ty, parse_result_unwrap_type(r)));
}

static struct parse_result*
parse_lam(struct parser_state *st) {
  struct parse_result *r;

  const struct token *lam;
  if (!expect_token(st, TOKEN_LAMBDA, &lam)) {
    return parse_result_error_expected(TOKEN_LAMBDA, lam);
  }

  const struct token *lpar;
  if (!expect_token(st, TOKEN_LPAREN, &lpar)) {
    return parse_result_error_expected(TOKEN_LPAREN, lpar);
  }

  const struct token *v;
  if (!expect_token(st, TOKEN_VAR, &v)) {
    return parse_result_error_expected(TOKEN_VAR, v);
  }

  const struct token *colon;
  if (!expect_token(st, TOKEN_COLON, &colon)) {
    return parse_result_error_expected(TOKEN_COLON, colon);
  }

  r = parse_type(st);
  if (r->tag != PARSE_RESULT_TYPE) return r;
  struct type *ty = parse_result_unwrap_type(r);

  const struct token *rpar;
  if (!expect_token(st, TOKEN_RPAREN, &rpar)) {
    return parse_result_error_expected(TOKEN_RPAREN, rpar);
  }

  const struct token *dot;
  if (!expect_token(st, TOKEN_DOT, &dot)) {
    return parse_result_error_expected(TOKEN_DOT, dot);
  }

  r = parse_rec(st);
  if (r->tag != PARSE_RESULT_TERM) return r;
  Term *t = parse_result_unwrap_term(r);
  return parse_result_term(term_lam(v->v, ty, t));
}

static struct parse_result*
parse_rec(struct parser_state *st) {
  const struct token *t = peek_token(st);
  struct parse_result *r = NULL;
  switch (t->type) {
  case TOKEN_VAR:
    r = parse_var(st);


@@ 48,26 228,47 @@ static Term* parse_rec(struct parser_state *st) {
    r = parse_lam(st);
    break;
  case TOKEN_LPAREN: {
    expect_token(st, TOKEN_LPAREN);
    const struct token *lpar;
    if (!expect_token(st, TOKEN_LPAREN, &lpar)) {
      return parse_result_error_expected(TOKEN_LPAREN, lpar);
    }

    r = parse_rec(st);
    expect_token(st, TOKEN_RPAREN);
    if (r->tag != PARSE_RESULT_TERM) return r;

    const struct token *rpar;
    if (!expect_token(st, TOKEN_RPAREN, &rpar)) {
      return parse_result_error_expected(TOKEN_RPAREN, rpar);
    }
    break;
  }
  case TOKEN_COLON:
  case TOKEN_ARR:
  case TOKEN_CONSTANT:
  case TOKEN_RPAREN:
  case TOKEN_DOT:
  case TOKEN_EOF:
    printf("parse_rec: unexpected token: ");
    token_puts(t);
    abort();
  default: g_assert_not_reached();
  }

  g_assert(r != NULL);
  if (r->tag != PARSE_RESULT_TERM) return r;

  t = peek_token(st);
  if (t->type == TOKEN_EOF || t->type == TOKEN_RPAREN) return r;
  return term_app(r, parse_rec(st));
  Term *tm = parse_result_unwrap_term(r);
  r = parse_rec(st);
  if (r->tag != PARSE_RESULT_TERM) {
    term_ref_dec(tm);
    return r;
  }

  return parse_result_term(term_app(tm, parse_result_unwrap_term(r)));
}

Term*
struct parse_result*
parse(const GArray *tokens) {
  struct parser_state st = {
    .tokens = tokens,

M lambda-c/src/parser.h => lambda-c/src/parser.h +31 -1
@@ 3,8 3,38 @@

#include <glib.h>

#include "lexer.h"
#include "term.h"

Term* parse(const GArray /* of struct token */ *tokens);
enum parse_result_tag {
  PARSE_RESULT_TERM,
  PARSE_RESULT_TYPE,
  PARSE_RESULT_ERROR_EXPECTED
};

struct parse_result {
  enum parse_result_tag tag;
  union {
    Term *term;
    struct type *type;
    struct { enum token_type expected; struct token got; } expected;
  };
};

struct parse_result* parse_result_term(Term *t); /* Takes a floating ref */
struct parse_result* parse_result_type(struct type *ty); /* Takes a floating ref */
struct parse_result* parse_result_error_expected(enum token_type expected,
                                                 const struct token *got);
/* Return the Term* in a struct parse_result (floating ref), and free
   the struct parse_result. abort() on parse failure. */
Term* parse_result_unwrap_term(struct parse_result *p);

/* Return the struct type* in a struct parse_result (floating ref),
   and free the struct parse_result. abort() on parse failure. */
struct type* parse_result_unwrap_type(struct parse_result *p);

void parse_result_free(struct parse_result *p);

struct parse_result* parse(const GArray /* of struct token */ *tokens);

#endif

M lambda-c/src/refcount.c => lambda-c/src/refcount.c +12 -3
@@ 35,17 35,26 @@ refcount_init_floating(struct refcount *rc) {
}

void
refcount_ref(struct refcount *rc) {
refcount_inc(struct refcount *rc) {
  rc->count++;
}

void
refcount_ref_sink(struct refcount *rc) {
refcount_sink(struct refcount *rc) {
  if (rc->floating) rc->floating = 0; else rc->count++;
}

void
refcount_float(struct refcount *rc) {
  if (rc->floating) {
    fprintf(stderr, "refcount_make_floating: already floating!\n");
    abort();
  }
  rc->floating = 1;
}

int
refcount_unref(struct refcount *rc) {
refcount_dec(struct refcount *rc) {
  rc->count--;
  if (rc->count == 0) {
    return 1;

M lambda-c/src/refcount.h => lambda-c/src/refcount.h +6 -3
@@ 27,10 27,13 @@ struct refcount {
void refcount_init(struct refcount *rc);
void refcount_init_floating(struct refcount *rc);

void refcount_ref(struct refcount *rc);
void refcount_ref_sink(struct refcount *rc);
void refcount_inc(struct refcount *rc);
void refcount_sink(struct refcount *rc);

/* Create a floating ref, if none exists. Abort if one does. */
void refcount_float(struct refcount *rc);

/* return 1 if the thing being counted should be freed. */
int refcount_unref(struct refcount *rc);
int refcount_dec(struct refcount *rc);

#endif

M lambda-c/src/term.c => lambda-c/src/term.c +21 -14
@@ 19,7 19,9 @@
#include "config.h"
#include "term.h"

Term*
#include "type.h"

static Term*
term_new(void) {
  struct term *r = g_new(struct term, 1);
  refcount_init_floating(&r->rc);


@@ 44,45 46,48 @@ term_app(Term *t1, Term *t2) {
}

Term*
term_lam(const gchar *v, Term *t) {
term_lam(const gchar *v, struct type *ty, Term *t) {
  struct term *r = term_new();
  r->tag = TERM_LAM;
  r->lam.v = g_strdup(v);
  r->lam.ty = type_ref_sink(ty);
  r->lam.t = term_ref_sink(t);
  return r;
}

Term*
term_ref(Term *t) {
  refcount_ref(&t->rc);
term_ref_inc(Term *t) {
  refcount_inc(&t->rc);
  return t;
}

Term*
term_ref_sink(Term *t) {
  refcount_ref_sink(&t->rc);
  refcount_sink(&t->rc);
  return t;
}

void
term_unref(Term *t) {
  if (!refcount_unref(&t->rc)) return;
term_ref_float(Term *t) {
  refcount_float(&t->rc);
}

void
term_ref_dec(Term *t) {
  if (!refcount_dec(&t->rc)) return;

  switch (t->tag) {
  case TERM_VAR:
    g_free(t->var.v);
    break;
  case TERM_APP:
    term_unref(t->app.t1);
    term_unref(t->app.t2);
    term_ref_dec(t->app.t1);
    term_ref_dec(t->app.t2);
    break;
  case TERM_LAM:
    g_free(t->lam.v);
    term_unref(t->lam.t);
    term_ref_dec(t->lam.t);
    break;
  default:
    fprintf(stderr, "term_unref: unexpected tag %d\n", t->tag);
    abort();
  }

  g_free(t);


@@ 113,7 118,9 @@ term_fput_app(const Term *t, FILE *fp) {
void
term_fput(const Term *t, FILE *fp) {
  if (t->tag == TERM_LAM) {
    fprintf(fp, "\\%s . ", t->lam.v);
    fprintf(fp, "\\(%s : ", t->lam.v);
    type_fput(t->lam.ty, fp);
    fprintf(fp, ") . ");
    term_fput(t->lam.t, fp);
  } else {
    term_fput_app(t, fp);

M lambda-c/src/term.h => lambda-c/src/term.h +6 -5
@@ 35,20 35,21 @@ typedef struct term {
  union {
    struct { gchar *v; } var;
    struct { struct term *t1; struct term *t2; } app;
    struct { gchar *v; struct term *t; } lam;
    struct { gchar *v; struct type *ty; struct term *t; } lam;
  };
} Term;

/* Copies const ghcar* args and takes the floating ref of Term* args. */
Term* term_var(const gchar *v);
Term* term_app(Term *t1, Term *t2);
Term* term_lam(const gchar *v, Term *t);
Term* term_lam(const gchar *v, struct type *ty, Term *t);

Term* term_ref(Term *t);
Term* term_ref_inc(Term *t);
Term* term_ref_sink(Term *t);
void term_unref(Term *t);
void term_ref_float(Term *t);
void term_ref_dec(Term *t);

G_DEFINE_AUTOPTR_CLEANUP_FUNC(Term, term_unref);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(Term, term_ref_dec);

void term_fput(const Term *t, FILE *fp);


A lambda-c/src/type.c => lambda-c/src/type.c +102 -0
@@ 0,0 1,102 @@
/*
 * lambda-c: minimal lambda calculus interpreter
 * Copyright (C) 2021  Jack Kelly
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "config.h"
#include "type.h"

#include <glib.h>

static struct type*
type_new(void) {
  struct type *r = g_new(struct type, 1);
  refcount_init_floating(&r->rc);
  return r;
}

struct type*
type_constant(const char *c) {
  struct type *r = type_new();
  r->tag = TYPE_CONSTANT;
  r->constant.c = g_strdup(c);
  return r;
}

struct type*
type_arr(struct type *t1, struct type *t2) {
  struct type *r = type_new();
  r->tag = TYPE_ARR;
  r->arr.t1 = type_ref_sink(t1);
  r->arr.t2 = type_ref_sink(t2);
  return r;
}

struct type*
type_ref_inc(struct type *t) {
  refcount_inc(&t->rc);
  return t;
}

struct type*
type_ref_sink(struct type *t) {
  refcount_sink(&t->rc);
  return t;
}

void
type_ref_float(struct type *t) {
  refcount_float(&t->rc);
}

void
type_ref_dec(struct type *t) {
  if (!refcount_dec(&t->rc)) return;

  switch (t->tag) {
  case TYPE_CONSTANT:
    g_free(t->constant.c);
    break;
  case TYPE_ARR:
    type_ref_dec(t->arr.t1);
    type_ref_dec(t->arr.t2);
    break;
  }

  g_free(t);
}

static void
type_fput_constant(const struct type *t, FILE *fp) {
  if (t->tag == TYPE_CONSTANT) {
    fprintf(fp, "%s", t->constant.c);
  } else {
    fprintf(fp, "(");
    type_fput(t, fp);
    fprintf(fp, ")");
  }
}

void
type_fput(const struct type *t, FILE *fp) {
  if (t->tag == TYPE_ARR) {
    type_fput_constant(t->arr.t1, fp);
    fprintf(fp, " -> ");
    type_fput(t->arr.t2, fp);
  } else {
    type_fput_constant(t, fp);
  }
}

A lambda-c/src/type.h => lambda-c/src/type.h +50 -0
@@ 0,0 1,50 @@
/*
 * lambda-c: minimal lambda calculus interpreter
 * Copyright (C) 2021  Jack Kelly
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef TYPE_H
#define TYPE_H

#include <stdio.h>
#include "refcount.h"

enum type_tag {
  TYPE_CONSTANT,
  TYPE_ARR
};

struct type {
  struct refcount rc;
  enum type_tag tag;
  union {
    struct { char *c; } constant;
    struct { struct type *t1; struct type *t2; } arr;
  };
};

/* Copies const char* args and takes floating ref ot struct type* args */
struct type* type_constant(const char *c);
struct type* type_arr(struct type *t1, struct type *t2);

struct type* type_ref_inc(struct type *t);
struct type* type_ref_sink(struct type *t);
void type_ref_float(struct type *t);
void type_ref_dec(struct type *t);

void type_fput(const struct type *t, FILE *fp);

#endif