Last active
July 21, 2025 16:10
-
-
Save DarinM223/6900d898df79078dcbaf6b27100e7596 to your computer and use it in GitHub Desktop.
Modeling various high level language features in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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