diff --git a/README.org b/README.org index 603a54e..c9361f8 100644 --- a/README.org +++ b/README.org @@ -8,6 +8,9 @@ Completed course from The exercises from the course have been worked out in this repo. This is my way of having the "certificate" for free. +The first the 3 chapters of the course can be done interactively on the website. + +[[./bootdev-c.png]] If you're reading this with the intention of completing the exercises yourself, just remove the directories from the =src= directory and recreate the directory @@ -21,10 +24,6 @@ Note: - In chapter 8, I renamed ~stack_t~ to ~my_stack_t~ due to a =std= naming conflict on macOS -The first the 3 chapters of the course can be done interactively on the website. - -[[./bootdev-c.png]] - ** Build instructions Compiling and running all the tests can be done with the following commands: diff --git a/src/9-1-snek-objects/main.c b/src/9-1-snek-objects/main.c new file mode 100644 index 0000000..121002b --- /dev/null +++ b/src/9-1-snek-objects/main.c @@ -0,0 +1,30 @@ +#include + +#include "munit.h" +#include "snekobject.h" + +munit_case(RUN, test_integer_constant, { + assert_int(INTEGER, ==, 0, "INTEGER is defined as 0"); +}); + +munit_case(RUN, test_integer_obj, { + snek_object_t *obj = malloc(sizeof(snek_object_t)); + obj->kind = INTEGER; + obj->data.v_int = 0; + assert_int(obj->kind, ==, INTEGER, "must be INTEGER type"); + assert_int(obj->data.v_int, ==, 0, "must equal zero"); + + free(obj); +}); + +int main() { + MunitTest tests[] = { + munit_test("/integer_constant", test_integer_constant), + munit_test("/integer_obj", test_integer_obj), + munit_null_test, + }; + + MunitSuite suite = munit_suite("object-integer-def", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/9-1-snek-objects/snekobject.h b/src/9-1-snek-objects/snekobject.h new file mode 100644 index 0000000..8c3c3e4 --- /dev/null +++ b/src/9-1-snek-objects/snekobject.h @@ -0,0 +1,13 @@ + +typedef enum SnekObjectKind { + INTEGER, +} snek_object_kind_t; + +typedef union SnekObjectData { + int v_int; +} snek_object_data_t; + +typedef struct SnekObject { + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; diff --git a/src/9-2-integer/main.c b/src/9-2-integer/main.c new file mode 100644 index 0000000..c85bb93 --- /dev/null +++ b/src/9-2-integer/main.c @@ -0,0 +1,40 @@ +#include "munit.h" +#include "snekobject.h" + +munit_case(RUN, test_positive, { + snek_object_t *int_object = new_snek_integer(42); + assert_int(int_object->data.v_int, ==, 42, "must allow positive numbers"); + + free(int_object); +}); + +munit_case(RUN, test_zero, { + snek_object_t *int_object = new_snek_integer(0); + + assert_int(int_object->kind, ==, INTEGER, "must be INTEGER type"); + assert_int(int_object->data.v_int, ==, 0, "must equal zero"); + + free(int_object); +}); + +munit_case(SUBMIT, test_negative, { + snek_object_t *int_object = new_snek_integer(-5); + + assert_int(int_object->kind, ==, INTEGER, "must be INTEGER type"); + assert_int(int_object->data.v_int, ==, -5, "must allow negative numbers"); + + free(int_object); +}); + +int main() { + MunitTest tests[] = { + munit_test("/positive", test_positive), + munit_test("/zero", test_zero), + munit_test("/negative", test_negative), + munit_null_test, + }; + + MunitSuite suite = munit_suite("object-integer", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/9-2-integer/snekobject.c b/src/9-2-integer/snekobject.c new file mode 100644 index 0000000..eaf7d24 --- /dev/null +++ b/src/9-2-integer/snekobject.c @@ -0,0 +1,11 @@ +#include + +#include "snekobject.h" + +snek_object_t *new_snek_integer(int value) { + snek_object_t *number = (snek_object_t *)malloc(sizeof(snek_object_t)); + if (number == NULL) return NULL; + number->kind = INTEGER; + number->data.v_int = value; + return number; +} diff --git a/src/9-2-integer/snekobject.h b/src/9-2-integer/snekobject.h new file mode 100644 index 0000000..0f62ac4 --- /dev/null +++ b/src/9-2-integer/snekobject.h @@ -0,0 +1,14 @@ +typedef enum SnekObjectKind { + INTEGER, +} snek_object_kind_t; + +typedef union SnekObjectData { + int v_int; +} snek_object_data_t; + +typedef struct SnekObject { + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; + +snek_object_t *new_snek_integer(int value); diff --git a/src/9-3-float/main.c b/src/9-3-float/main.c new file mode 100644 index 0000000..bda0e4d --- /dev/null +++ b/src/9-3-float/main.c @@ -0,0 +1,45 @@ +#include + +#include "munit.h" +#include "snekobject.h" + +munit_case(RUN, test_positive, { + snek_object_t *obj = new_snek_float(42); + assert_float(obj->data.v_float, ==, 42, "Must accept positive values"); + + free(obj); + // assert(boot_all_freed()); +}); + +munit_case(SUBMIT, test_zero, { + snek_object_t *obj = new_snek_float(0.0); + + assert_float(obj->kind, ==, FLOAT, "Must set type to FLOAT"); + assert_float(obj->data.v_float, ==, 0.0, "Must accept 0.0"); + + free(obj); + // assert(boot_all_freed()); +}); + +munit_case(SUBMIT, test_negative, { + snek_object_t *obj = new_snek_float(-5.0); + + assert_float(obj->kind, ==, FLOAT, "Must set type to FLOAT"); + assert_float(obj->data.v_float, ==, -5.0, "Must accept negative numbers"); + + free(obj); + // assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/positive", test_positive), + munit_test("/zero", test_zero), + munit_test("/negative", test_negative), + munit_null_test, + }; + + MunitSuite suite = munit_suite("object-float", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/9-3-float/snekobject.c b/src/9-3-float/snekobject.c new file mode 100644 index 0000000..cc6ad48 --- /dev/null +++ b/src/9-3-float/snekobject.c @@ -0,0 +1,24 @@ +#include + +#include "snekobject.h" + +snek_object_t *new_snek_float(float value) { + snek_object_t *obj = (snek_object_t *)malloc(sizeof(snek_object_t)); + if (obj == NULL) return NULL; + obj->kind = FLOAT; + obj->data.v_float = value; + return obj; +} + +// don't touch below this line + +snek_object_t *new_snek_integer(int value) { + snek_object_t *obj = malloc(sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + + obj->kind = INTEGER; + obj->data.v_int = value; + return obj; +} diff --git a/src/9-3-float/snekobject.h b/src/9-3-float/snekobject.h new file mode 100644 index 0000000..3ce1045 --- /dev/null +++ b/src/9-3-float/snekobject.h @@ -0,0 +1,17 @@ +typedef enum SnekObjectKind { + INTEGER, + FLOAT, +} snek_object_kind_t; + +typedef union SnekObjectData { + int v_int; + float v_float; +} snek_object_data_t; + +typedef struct SnekObject { + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; + +snek_object_t *new_snek_integer(int value); +snek_object_t *new_snek_float(float value); diff --git a/src/9-4-string/main.c b/src/9-4-string/main.c new file mode 100644 index 0000000..e0f8269 --- /dev/null +++ b/src/9-4-string/main.c @@ -0,0 +1,41 @@ +#include + +#include "munit.h" +#include "snekobject.h" + +munit_case(RUN, test_str_copied, { + char *input = "Hello, World!"; + snek_object_t *obj = new_snek_string(input); + + assert_int(obj->kind, ==, STRING, "Must be a string!"); + + // Should not have pointers be the same, otherwise we didn't copy the value. + assert_ptr_not_equal( + obj->data.v_string, input, "You need to copy the string." + ); + + // But should have the same data! + // This way the object can free it's own memory later. + assert_string_equal( + obj->data.v_string, input, "Should copy string correctly" + ); + + // Should allocate memory for the string with null terminator. + // assert_int_equal(boot_alloc_size(), 22, "Must allocate memory for string"); + + // Free the string, and then free the object. + free(obj->data.v_string); + free(obj); + // assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/copies_value", test_str_copied), + munit_null_test, + }; + + MunitSuite suite = munit_suite("object-string", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/9-4-string/snekobject.c b/src/9-4-string/snekobject.c new file mode 100644 index 0000000..97404ad --- /dev/null +++ b/src/9-4-string/snekobject.c @@ -0,0 +1,46 @@ +#include +#include +#include + +#include "snekobject.h" + +snek_object_t *new_snek_string(char *value) { + snek_object_t *obj = malloc(sizeof(snek_object_t)); + if (obj == NULL) return NULL; + + size_t size = strlen(value); + char *str = (char *)malloc(sizeof(char) * size + 1); + if (str == NULL) { + free(obj); + return NULL; + } + + strcpy(str, value); + obj->kind = STRING; + obj->data.v_string = str; + return obj; +} + +// don't touch below this line + +snek_object_t *new_snek_integer(int value) { + snek_object_t *obj = malloc(sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + + obj->kind = INTEGER; + obj->data.v_int = value; + return obj; +} + +snek_object_t *new_snek_float(float value) { + snek_object_t *obj = malloc(sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + + obj->kind = FLOAT; + obj->data.v_float = value; + return obj; +} diff --git a/src/9-4-string/snekobject.h b/src/9-4-string/snekobject.h new file mode 100644 index 0000000..eef6576 --- /dev/null +++ b/src/9-4-string/snekobject.h @@ -0,0 +1,20 @@ +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, +} snek_object_kind_t; + +typedef union SnekObjectData { + int v_int; + float v_float; + char* v_string; +} snek_object_data_t; + +typedef struct SnekObject { + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; + +snek_object_t *new_snek_integer(int value); +snek_object_t *new_snek_float(float value); +snek_object_t *new_snek_string(char *value); diff --git a/src/9-5-vector3/main.c b/src/9-5-vector3/main.c new file mode 100644 index 0000000..b2958c5 --- /dev/null +++ b/src/9-5-vector3/main.c @@ -0,0 +1,81 @@ +#include + +#include "munit.h" +#include "snekobject.h" + +munit_case(RUN, test_returns_null, { + snek_object_t *vec = new_snek_vector3(NULL, NULL, NULL); + + assert_null(vec, "Should return null when input is null"); + + // assert(boot_all_freed()); +}); + + +munit_case(RUN, test_vec_multiple_objects, { + snek_object_t *x = new_snek_integer(1); + snek_object_t *y = new_snek_integer(2); + snek_object_t *z = new_snek_integer(3); + snek_object_t *vec = new_snek_vector3(x, y, z); + + assert_ptr_not_null(vec, "should allocate a new object"); + + // Vectors should not copy objects, they get the reference to the objects. + assert_ptr(x, ==, vec->data.v_vector3.x, "should reference x"); + assert_ptr(y, ==, vec->data.v_vector3.y, "should reference y"); + assert_ptr(z, ==, vec->data.v_vector3.z, "should reference z"); + + // Assert we have integer values correct + assert_int(vec->data.v_vector3.x->data.v_int, ==, 1, "should have correct x"); + assert_int(vec->data.v_vector3.y->data.v_int, ==, 2, "should have correct y"); + assert_int(vec->data.v_vector3.z->data.v_int, ==, 3, "should have correct z"); + + // Free all of our objects. + free(x); + free(y); + free(z); + free(vec); + // assert(boot_all_freed()); +}); + +munit_case(SUBMIT, test_vec_same_object, { + snek_object_t *i = new_snek_integer(1); + snek_object_t *vec = new_snek_vector3(i, i, i); + + assert_ptr_not_null(vec, "should allocate a new object"); + + // Vectors should not copy objects, they get the reference to the objects. + assert_ptr(i, ==, vec->data.v_vector3.x, "should reference x"); + assert_ptr(i, ==, vec->data.v_vector3.y, "should reference y"); + assert_ptr(i, ==, vec->data.v_vector3.z, "should reference z"); + + // Assert we have integer values correct + assert_int(vec->data.v_vector3.x->data.v_int, ==, 1, "should have correct x"); + assert_int(vec->data.v_vector3.y->data.v_int, ==, 1, "should have correct y"); + assert_int(vec->data.v_vector3.z->data.v_int, ==, 1, "should have correct z"); + + i->data.v_int = 2; + + // Assert we have integer values correct, after update + assert_int(vec->data.v_vector3.x->data.v_int, ==, 2, "should have correct x"); + assert_int(vec->data.v_vector3.y->data.v_int, ==, 2, "should have correct y"); + assert_int(vec->data.v_vector3.z->data.v_int, ==, 2, "should have correct z"); + + // Free all of our objects. + free(i); + free(vec); + // assert(boot_all_freed()); +}); + +int main() { + MunitTest tests[] = { + munit_test("/returns_null", test_returns_null), + munit_test("/multiple_objects", test_vec_multiple_objects), + munit_test("/same_object", test_vec_same_object), + munit_null_test, + }; + + MunitSuite suite = munit_suite("object-vector", tests); + + return munit_suite_main(&suite, NULL, 0, NULL); +} diff --git a/src/9-5-vector3/snekobject.c b/src/9-5-vector3/snekobject.c new file mode 100644 index 0000000..3f9db3c --- /dev/null +++ b/src/9-5-vector3/snekobject.c @@ -0,0 +1,69 @@ +#include +#include + +#include "snekobject.h" + +snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z) +{ + if (x == NULL || y == NULL || z == NULL) return NULL; + + snek_object_t *obj = (snek_object_t *)malloc(sizeof(snek_object_t)); + if (obj == NULL) return NULL; + + snek_vector_t *vec = (snek_vector_t *)malloc(sizeof(snek_vector_t)); + if (vec == NULL) { + free(obj); + return NULL; + } + + vec->x = x; + vec->y = y; + vec->z = z; + obj->kind = VECTOR3; + obj->data.v_vector3 = *vec; + return obj; +} + +// don't touch below this line + +snek_object_t *new_snek_integer(int value) { + snek_object_t *obj = malloc(sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + + obj->kind = INTEGER; + obj->data.v_int = value; + return obj; +} + +snek_object_t *new_snek_float(float value) { + snek_object_t *obj = malloc(sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + + obj->kind = FLOAT; + obj->data.v_float = value; + return obj; +} + +snek_object_t *new_snek_string(char *value) { + snek_object_t *obj = malloc(sizeof(snek_object_t)); + if (obj == NULL) { + return NULL; + } + + int len = strlen(value); + char *dst = malloc(len + 1); + if (dst == NULL) { + free(obj); + return NULL; + } + + strcpy(dst, value); + + obj->kind = STRING; + obj->data.v_string = dst; + return obj; +} diff --git a/src/9-5-vector3/snekobject.h b/src/9-5-vector3/snekobject.h new file mode 100644 index 0000000..96d42c6 --- /dev/null +++ b/src/9-5-vector3/snekobject.h @@ -0,0 +1,31 @@ +typedef struct SnekObject snek_object_t; + +typedef enum SnekObjectKind { + INTEGER, + FLOAT, + STRING, + VECTOR3, +} snek_object_kind_t; + +typedef struct SnekVector { + snek_object_t *x; + snek_object_t *y; + snek_object_t *z; +} snek_vector_t; + +typedef union SnekObjectData { + int v_int; + float v_float; + char* v_string; + snek_vector_t v_vector3; +} snek_object_data_t; + +typedef struct SnekObject { + snek_object_kind_t kind; + snek_object_data_t data; +} snek_object_t; + +snek_object_t *new_snek_integer(int value); +snek_object_t *new_snek_float(float value); +snek_object_t *new_snek_string(char *value); +snek_object_t *new_snek_vector3(snek_object_t *x, snek_object_t *y, snek_object_t *z); diff --git a/src/munit.h b/src/munit.h index e2389c0..97307c3 100644 --- a/src/munit.h +++ b/src/munit.h @@ -105,8 +105,14 @@ #define munit_assert_ptr_not_equal(A, B, MSG) \ munit_assert_ptr(A, !=, B) +#undef assert_ptr +#define assert_ptr(A, OP, B, MSG) munit_assert_ptr(A, OP, B) + #undef assert_ptr_not_null #define assert_ptr_not_null(PTR, MSG) munit_assert_not_null(PTR, MSG) #undef assert_ptr_equal #define assert_ptr_equal(A, B, MSG) munit_assert_ptr_equal(A, B) + +#undef assert_ptr_not_equal +#define assert_ptr_not_equal(A, B, MSG) munit_assert_ptr_not_equal(A, B, MSG)