Skip to content

Instantly share code, notes, and snippets.

@DarinM223
Last active July 21, 2025 16:10
Show Gist options
  • Select an option

  • Save DarinM223/6900d898df79078dcbaf6b27100e7596 to your computer and use it in GitHub Desktop.

Select an option

Save DarinM223/6900d898df79078dcbaf6b27100e7596 to your computer and use it in GitHub Desktop.
Modeling various high level language features in C
#include <stdio.h>
enum bop {
BOP_PLUS,
BOP_MINUS,
BOP_TIMES,
};
typedef struct ast_exp ast_exp;
struct ast_exp {
enum {
AST_INT,
AST_VAR,
AST_LAM,
AST_APP,
AST_BOP,
AST_IF
} type;
// Unnamed structs/unions supported in C11, not C99 without extensions.
union {
int intt;
char *var;
struct {
char *var;
struct ast_exp *body;
} lam;
struct {
struct ast_exp *fn;
struct ast_exp *param;
} app;
struct {
enum bop bop;
struct ast_exp *lhs;
struct ast_exp *rhs;
} bop;
struct {
struct ast_exp *cond;
struct ast_exp *thenn;
struct ast_exp *elsee;
} iff;
};
};
void print_ast_exp(ast_exp *exp)
{
// gcc gives warning if not exhaustive
switch (exp->type) {
case AST_INT:
printf("%d", exp->intt);
break;
case AST_VAR:
printf("%s", exp->var);
break;
case AST_LAM:
printf("(\\%s -> ", exp->lam.var);
print_ast_exp(exp->lam.body);
printf(")");
break;
case AST_APP:
printf("(");
print_ast_exp(exp->app.fn);
printf(" ");
print_ast_exp(exp->app.param);
printf(")");
break;
case AST_BOP: {
const char *bop;
switch (exp->bop.bop) {
case BOP_PLUS:
bop = "+";
break;
case BOP_MINUS:
bop = "-";
break;
case BOP_TIMES:
bop = "*";
break;
}
print_ast_exp(exp->bop.lhs);
printf(" %s ", bop);
print_ast_exp(exp->bop.rhs);
break;
}
case AST_IF:
printf("if ");
print_ast_exp(exp->iff.cond);
printf(" then ");
print_ast_exp(exp->iff.thenn);
printf(" else ");
print_ast_exp(exp->iff.elsee);
break;
}
}
// gcc -std=c11 -Wall -Wextra -Wpedantic adt.c
int main()
{
ast_exp lhs = {AST_INT, .intt = 2};
ast_exp rhs = {AST_INT, .intt = 3};
ast_exp exp = {AST_BOP, .bop = {BOP_PLUS, &lhs, &rhs}};
print_ast_exp(&exp);
printf("\n");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
typedef struct fresh_closure fresh_closure;
struct fresh_closure {
int (*call)(fresh_closure *);
};
struct my_fresh_closure {
int (*call)();
// C Standard: All identifiers that begin with an underscore and either
// an uppercase letter or another underscore are always reserved for any
// use. Also any global is not allowed to begin with an underscore. This
// is an underscore with a lowercase letter inside a struct so it should
// be ok.
int _counter;
};
struct my_fresh_closure2 {
int (*call)();
int _incrementer;
int _counter;
};
static int fresh_impl(struct my_fresh_closure *clos)
{
return clos->_counter++;
}
static int fresh_impl2(struct my_fresh_closure2 *clos)
{
clos->_counter += clos->_incrementer++;
return clos->_counter;
}
fresh_closure *global_fresh = (fresh_closure *)&(struct my_fresh_closure){
.call = fresh_impl,
._counter = 0,
};
fresh_closure *fresh_alloc(void)
{
struct my_fresh_closure2 *clos = malloc(sizeof(*clos));
clos->_incrementer = 1;
clos->_counter = 0;
clos->call = fresh_impl2;
// This should be ok because the C Standard says that there cannot
// be any padding before the first element of the struct, and the
// only usage of fresh_closure is its first element call.
return (fresh_closure *)clos;
}
int fresh(void)
{
return global_fresh->call(global_fresh);
}
void print_fresh(const char *desc, fresh_closure *clos)
{
printf("%s fresh: %d\n", desc, clos->call(clos));
printf("%s fresh: %d\n", desc, clos->call(clos));
printf("%s fresh: %d\n", desc, clos->call(clos));
}
// gcc -std=c11 -Wall -Wextra -Wpedantic closure.c
int main()
{
print_fresh("global", global_fresh);
fresh_closure *my_fresh = fresh_alloc();
print_fresh("local", my_fresh);
free(my_fresh);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
typedef struct person person;
typedef struct student student;
struct person_vtable {
// Function with () means unknown number of parameters,
// (void) is explicitly no parameters.
char *(*get_name)();
int (*get_age)();
};
struct person {
const struct person_vtable *vtable;
char *name;
int age;
};
char *person_get_name(person *person)
{
return person->name;
}
int person_get_age(person *person)
{
return person->age;
}
struct student {
// student can be safely casted to person because there is guaranteed
// to not be any padding between the start of the struct
// and the first element.
person parent;
char grade;
};
int student_get_age(student *student)
{
// Can call student's data and vtable functions inside method.
printf("Student with name %s and grade: %c\n",
((person *)student)->vtable->get_name(student), student->grade);
return student->parent.age;
}
// Add comma after last field so clangformat doesn't put everything in one line.
const struct person_vtable person_vtable = {
.get_name = person_get_name,
.get_age = person_get_age,
};
const struct person_vtable student_vtable = {
.get_name = person_get_name,
.get_age = student_get_age,
};
person *person_alloc(char *name, int age)
{
person *p = malloc(sizeof(person));
*p = (person){&person_vtable, name, age};
return p;
}
void print_person(person *person)
{
printf("Name: %s\n", person->vtable->get_name(person));
printf("Age: %d\n", person->vtable->get_age(person));
}
// gcc -std=c11 -Wall -Wextra -Wpedantic oop.c
int main()
{
person bob = {&person_vtable, .name = "bob", .age = 21};
student jane = {{&student_vtable, .name = "jane", .age = 19}, .grade = 'A'};
print_person(&bob);
print_person((person *)&jane);
person *joe = person_alloc("joe", 30);
print_person(joe);
free(joe);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment