diff --git a/.gitignore b/.gitignore index b6fa90d..de4fa40 100644 --- a/.gitignore +++ b/.gitignore @@ -59,8 +59,7 @@ CMake* cmake* *.bak -# Julia docs -docs/build/* docs/Manifest.toml quibble +qbinfo diff --git a/CMakeLists.txt b/CMakeLists.txt index e6250e9..b6590fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,21 @@ cmake_minimum_required (VERSION 3.8) +set(CMAKE_C_COMPILER clang) project (Quibble LANGUAGES C) find_package(OpenCL REQUIRED) +if (MSVC) + add_compile_options(/W4) +else() + add_compile_options(-Wall -Wextra -Wpedantic -Wno-varargs) +endif() + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +# QUIBBLE + add_executable( quibble include/macros.h @@ -10,10 +23,39 @@ add_executable( include/errors.h src/errors.c - include/demo_functions.h - src/demo_functions.c + include/io.h + src/io.c + + include/quibble_args.h + src/quibble_args.c + + include/quibble_program.h + src/quibble_verses.c + src/quibble_stanzas.c + src/quibble_poems.c + src/quibble_program.c + + test/tests.h + test/io_tests.c + test/program_tests.c src/demo.c ) target_link_libraries(quibble OpenCL::OpenCL) + +# QBINFO + +add_executable( + qbinfo + + include/errors.h + src/errors.c + + include/qbinfo.h + src/qbinfo.c +) + +target_link_libraries(qbinfo OpenCL::OpenCL) + +execute_process(COMMAND bash ${PROJECT_SOURCE_DIR}/configure) diff --git a/clean b/clean new file mode 100755 index 0000000..659840f --- /dev/null +++ b/clean @@ -0,0 +1,9 @@ +#!/bin/bash +if [ -z ${XDG_CONFIG_HOME+x} ]; \ + then config_dir="$HOME/.config/quibble"; echo "XDG_CONFIG_HOME is unset, attempting to clean $config_dir"; \ + else config_dir="$XDG_CONFIG_HOME/quibble"; echo "attempting to clean $config_dir..."; \ +fi + +rm -r $config_dir + +echo "done!" diff --git a/configure b/configure new file mode 100755 index 0000000..e19305f --- /dev/null +++ b/configure @@ -0,0 +1,11 @@ +#!/bin/bash +cd $(dirname $0) +if [ -z ${XDG_CONFIG_HOME+x} ]; \ + then config_dir="$HOME/.config/quibble"; echo "XDG_CONFIG_HOME is unset, using $config_dir"; \ + else config_dir="$XDG_CONFIG_HOME/quibble"; echo "using $config_dir..."; \ +fi + +mkdir -p $config_dir/scribbles +cp scribbles/* $config_dir/scribbles/. + +echo "done!" diff --git a/include/demo_functions.h b/include/demo_functions.h deleted file mode 100644 index bdbc096..0000000 --- a/include/demo_functions.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef DEMO_FX_H -#define DEMO_FX_H - -#include -#include - -void define_preamble(char *final_fx); - -void define_postamble(char *final_fx); - -void define_functions(char *final_fx, int n); -#endif diff --git a/include/errors.h b/include/errors.h index 8a8ee56..386361e 100644 --- a/include/errors.h +++ b/include/errors.h @@ -7,6 +7,18 @@ #include "macros.h" #include +#include +#include + +// QBT -> Quibble Text +#define QBT_RED "\x1b[31m" +#define QBT_GREEN "\x1b[32m" +#define QBT_YELLOW "\x1b[33m" +#define QBT_BLUE "\x1b[34m" +#define QBT_MAGENTA "\x1b[35m" +#define QBT_CYAN "\x1b[36m" +#define QBT_RESET "\x1b[0m" + void cl_check(cl_int error); const char *get_cl_error_string(cl_int error); diff --git a/include/io.h b/include/io.h new file mode 100644 index 0000000..e861c03 --- /dev/null +++ b/include/io.h @@ -0,0 +1,42 @@ +#ifndef IO_H +#define IO_H + +#include +#include +#include +#include + +typedef struct { + unsigned char red; + unsigned char green; + unsigned char blue; + unsigned char alpha; +} quibble_pixel; + +quibble_pixel qb_zero_pixel(void); +quibble_pixel *qb_create_pixel_array(int height, int width); + +char *qb_config_file(char *path); + +size_t qb_find_type_size(char *type); + +// String Manipulation +bool qb_is_space(char a); +char *qb_copy(char *buffer); +char *qb_strip_spaces(char *body, int start_index, int end_index); +int qb_find_next_char(char *body, int current_index, char a); +int qb_find_next_string(char *body, int current_index, char *a); +int qb_find_matching_char(char *body, int body_size, int current_index, + char a, char b); + +void qb_replace_char(char *body, int body_size, char a, char b); +void qb_replace_next_char(char *content, int index, char a, char b); +void qb_replace_char_if_proceeding(char *content, char *query, char a, char b); + +int qb_find_occurrences(char *query, char *body); + +// QBINLINE +bool qb_is_inlined(char *verse); +void qb_preprocess_content(char *verse); + +#endif diff --git a/include/macros.h b/include/macros.h index 0818ab6..95b857e 100644 --- a/include/macros.h +++ b/include/macros.h @@ -1,9 +1,17 @@ #ifndef MACROS_H #define MACROS_H -//#define MAX_SOURCE_SIZE (0x100000) +// These numbers are arbitrarily set +// #define MAX_SOURCE_SIZE (0x100000) #define MAX_SOURCE_SIZE (0x999999) +#define MAX_PROLOGUE_SIZE (0x100000) +#define MAX_META_SIZE (0x100000) +// I might want to ise 1.2 in the end... #define CL_TARGET_OPENCL_VERSION 300 +//#define CL_TARGET_OPENCL_VERSION 120 + +// Delay Compilation +#define QBINLINE(...) "// QBINLINE GENERATED\n" #__VA_ARGS__ "\0" #endif diff --git a/include/qbinfo.h b/include/qbinfo.h new file mode 100644 index 0000000..f3c4eb3 --- /dev/null +++ b/include/qbinfo.h @@ -0,0 +1,12 @@ +#ifndef QB_INFO_H +#define QB_INFO_H + +#include "errors.h" +#include "macros.h" +#include + +char *get_device_name(cl_device_id device_id); +char *get_platform_name(cl_platform_id platform_id); +void qb_list_devices(void); + +#endif diff --git a/include/quibble_args.h b/include/quibble_args.h new file mode 100644 index 0000000..1ed9d3d --- /dev/null +++ b/include/quibble_args.h @@ -0,0 +1,51 @@ +#ifndef QUIBBLE_ARGS_H +#define QUIBBLE_ARGS_H + +#include +#include +#include + +#include "macros.h" +#include "io.h" + +typedef struct{ + char *type; + char *variable; +} quibble_arg; + +typedef struct{ + char *type; + char *variable; + char *value; +} quibble_kwarg; + +void qb_find_type_arg(char *arg, char **type, char **variable); +void qb_parse_arg(quibble_arg *qa, char *arg); +void qb_parse_kwarg(quibble_kwarg *qk, char *lhs, char *rhs); + +// Args / Kwargs +int qb_find_number_of_kwargs(char *config); +int qb_find_number_of_args(char *config); + +quibble_arg *qb_parse_args(char *config, int num_entries); +quibble_kwarg *qb_parse_kwargs(char *config, int num_entries); + +int qb_find_arg_index(quibble_arg *arg, int n, char *variable); +int qb_find_kwarg_index(quibble_kwarg *qk, int n, char *variable); + +// prologue +char *qb_create_prologue(char *config, char *name, + quibble_arg *qargs, int num_args, + quibble_kwarg *qkwargs, int num_kwargs); + +// Free +void qb_free_arg(quibble_arg arg); +void qb_free_kwarg(quibble_kwarg qkwarg); +void qb_free_arg_array(quibble_arg *args, int n); +void qb_free_kwarg_array(quibble_kwarg *qkwargs, int n); + +// Printing +void qb_print_arg(quibble_arg qa); +void qb_print_kwarg(quibble_kwarg qk); + +#endif diff --git a/include/quibble_program.h b/include/quibble_program.h new file mode 100644 index 0000000..31488aa --- /dev/null +++ b/include/quibble_program.h @@ -0,0 +1,136 @@ +#ifndef QUIBBLE_PROGRAM_H +#define QUIBBLE_PROGRAM_H + +#include +#include +#include +#include + +#include "macros.h" +#include +#include "io.h" +#include "errors.h" +#include "quibble_args.h" + +typedef struct{ + char *body; + char *name; + quibble_kwarg *kwargs; + quibble_arg *args; + int num_kwargs; + int num_args; +} quibble_verse; + +typedef struct{ + quibble_kwarg *kwargs; + int num_kwargs; + quibble_arg *args; + int num_args; + char *name; + char *prologue; + char *epilogue; +} quibble_stanza; + +typedef struct{ + quibble_arg *args; + int num_args; + char *body; + char *name; +} quibble_poem; + +typedef struct{ + char *everything_else; + char *body; + quibble_verse *verse_list; + quibble_stanza *stanza_list; + quibble_poem *poem_list; + int num_verses; + int num_stanzas; + int num_poems; + + // CL stuff + cl_platform_id *platform_ids; + cl_device_id *device_ids; + cl_uint num_devices; + cl_uint num_platforms; + int chosen_platform; + int chosen_device; + cl_context context; + cl_command_queue command_queue; + cl_program program; + cl_kernel *kernels; + bool configured; + +} quibble_program; + +// OpenCL Interface +char *get_device_name(cl_device_id device_id); +char *get_platform_name(cl_platform_id platform_id); +void qb_find_platforms(quibble_program *qp); +void qb_find_devices(quibble_program *qp); +void qb_configure_program(quibble_program *qp, int platform, int device); +void qb_run(quibble_program qp, char *kernel_name, + size_t global_item_size, + size_t local_item_size); +void qb_set_arg(quibble_program *qp, char *poem, char *arg, void *data); +void qb_set_args(quibble_program *qp, char *poem, int n, ...); + + +// string manip +bool qb_is_stanza(char *stanza, int offset); +bool qb_is_poem(char *poem, int offset); +bool qb_is_verse(char *verse, int offset); + +// Reads an input file and parses everything into verses or OCL functions +quibble_program qb_parse_program(char *buffer); +quibble_program qb_parse_program_file(char *filename); +quibble_verse qb_find_verse(quibble_program qp, char *verse_name); +quibble_stanza qb_find_stanza(quibble_program qp, char *stanza_name); +quibble_poem qb_find_poem(quibble_program qp, char *poem_name); +int qb_find_verse_index(quibble_program qp, char *verse_name); +int qb_find_stanza_index(quibble_program qp, char *stanza_name); +int qb_find_poem_index(quibble_program qp, char *poem_name); + +// Parsing from file +quibble_stanza qb_parse_stanza(char *stanza); +quibble_poem qb_parse_poem(char *stanza); +quibble_verse qb_parse_verse(char *verse); + +// Build / expansion +bool qb_find_keyword(quibble_program qp, char *keyword); +bool qb_find_keyword_in_verses(quibble_program qp, char *keyword); +bool qb_find_keyword_in_stanzas(quibble_program qp, char *keyword); +bool qb_find_keyword_in_poems(quibble_program qp, char *keyword); +char *qb_create_prologue(char *config, char *name, + quibble_arg *args, int num_args, + quibble_kwarg *qkwargs, int num_kwargs); +void qb_build_program(quibble_program *qp); +void qb_rebuild_program(quibble_program *qp); +char *qb_expand_verse(quibble_program qp, char* verse_name, char *prologue); +char *qb_expand_stanza(quibble_program qp, + char* stanza_name, char *prologue, char *body); +char *qb_expand_poem(quibble_program qp, int index); + +void qb_rebuild_program(quibble_program *qp); +void qb_build_program(quibble_program *qp); + +// Free +void qb_free_arg(quibble_arg arg); +void qb_free_kwarg(quibble_kwarg qkwarg); +void qb_free_verse(quibble_verse qv); +void qb_free_stanza(quibble_stanza qs); +void qb_free_poem(quibble_poem qp); +void qb_free_kwarg_array(quibble_kwarg *qkwargs, int n); +void qb_free_arg_array(quibble_arg *args, int n); +void qb_free_program(quibble_program qp); + +// Printing +void qb_print_arg(quibble_arg qa); +void qb_print_kwarg(quibble_kwarg qk); +void qb_print_verse(quibble_verse qv); +void qb_print_stanza(quibble_stanza qs); +void qb_print_poem(quibble_poem qp); +void qb_print_program(quibble_program qp); +void print_cl_info(quibble_program qp); + +#endif diff --git a/scribbles/example.qbl b/scribbles/example.qbl new file mode 100644 index 0000000..3227bed --- /dev/null +++ b/scribbles/example.qbl @@ -0,0 +1,46 @@ +void simple_print(){ + printf("printing from kernel...\n"); +} + +__verse nothing(void){} +__stanza nothing(void){} +__poem nothing(void){} + +__verse no_args(void){// do stuff verse} +__stanza no_args(void){// do stuff stanza} +__poem no_args(void){// do stuff poem} + +__verse no_body(int a){} +__stanza no_body(int a){} +__poem no_body(int a){} + +__verse mult_by(__global float *verse_a | float factor = 8;){ + verse_a[_idx] *= factor; +} + +void is_this_found(){}; + +__stanza stanza_check(__global float *stanza_a, + __global float *stanza_b, + __global float *stanza_c | float r = 1.0;){ + if (_idx < array_size){ + stanza_c[_idx] = stanza_a[_idx] + stanza_b[_idx]; + __split_stanza(); + stanza_c[_idx] += 1; // checking comments + } // checking comments +} + +__poem poem_check(__global float *a, + __global float *b, + __global float *c, + int stanza_num, + int array_size){ + if (stanza_num == 0){ + @SCALL stanza_check(a, b, c | r = 5;){ + @VCALL mult_by(c | factor = 2;); + } + if (_idx < array_size){ + @VCALL mult_by(c | factor = 0.5;); + } + } +} diff --git a/src/demo.c b/src/demo.c index 1e5b303..64aa2fa 100644 --- a/src/demo.c +++ b/src/demo.c @@ -11,152 +11,19 @@ Purpose: This file is a quick example of how we might read function fragments in #include "../include/macros.h" #include -#include "../include/demo_functions.h" #include "../include/errors.h" +#include "../include/quibble_program.h" +#include "../test/tests.h" -int main(){ - int array_size = 10; - float *a = (float*)malloc(sizeof(float)*array_size); +int main(void){ - for (size_t i = 0; i < array_size; ++i){ - a[i] = 0; - } -/* - char filename[50] = "demo.cl"; - FILE *kernel_file = fopen(filename, "r"); + // Tests + quibble_io_tests(); + quibble_arg_parsing_tests(); + quibble_program_tests(); - if (!kernel_file){ - fprintf(stderr, "No file %s found!\n", filename); - exit(-1); - } - - char *kernel_source = (char*)malloc(MAX_SOURCE_SIZE); - size_t kernel_size = fread(kernel_source, 1, MAX_SOURCE_SIZE, kernel_file); - fclose(kernel_file); -*/ - - // Creating Kernel String - char *kernel_source = (char*)malloc(MAX_SOURCE_SIZE); - define_preamble(kernel_source); - define_functions(kernel_source, 100); - define_postamble(kernel_source); - - size_t kernel_size = strlen(kernel_source); - //printf("%s \n %d\n", kernel_source, kernel_size); - - - // OpenCL specific device info - cl_platform_id platform_id = NULL; - cl_device_id device_id = NULL; - cl_uint num_devices; - cl_uint num_platforms; - - // For error propagation - cl_int err; - cl_check( - clGetPlatformIDs(1, &platform_id, &num_platforms) - ); - cl_check( - clGetDeviceIDs(platform_id, - CL_DEVICE_TYPE_DEFAULT, - 1, - &device_id, - &num_devices) - ); - - cl_context context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &err); - cl_check(err); - - cl_command_queue command_queue = clCreateCommandQueueWithProperties( - context, - device_id, - 0, - &err - ); - cl_check(err); - - // creating d_a and copying to GPU - cl_mem d_a = clCreateBuffer(context, - CL_MEM_READ_WRITE, - array_size * sizeof(float), - NULL, - &err); - cl_check(err); - - cl_check( - clEnqueueWriteBuffer(command_queue, - d_a, - CL_TRUE, - 0, - array_size * sizeof(float), - a, - 0, - NULL, - NULL) - ); - - // Create program - cl_program program = clCreateProgramWithSource(context, - 1, - (const char**)&kernel_source, - (const size_t *)&kernel_size, - &err); - cl_check(err); - - err = clBuildProgram(program, 1, &device_id, NULL, NULL, NULL); - cl_check_program(err, program, device_id); - - cl_kernel kernel = clCreateKernel(program, "demo", &err); - cl_check(err); - - cl_check( - clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&d_a) - ); - cl_check( - clSetKernelArg(kernel, 1, sizeof(int), &array_size) - ); - - size_t global_item_size = array_size; - size_t local_item_size = array_size; - - cl_check( - clEnqueueNDRangeKernel(command_queue, - kernel, - 1, - NULL, - &global_item_size, - &local_item_size, - 0, - NULL, - NULL) - ); - - cl_check( - clEnqueueReadBuffer(command_queue, - d_a, - CL_TRUE, - 0, - array_size * sizeof(float), - a, - 0, - NULL, - NULL) - ); - - for (int i = 0; i < array_size; ++i){ - printf("%f\n", a[i]); - } - - cl_check(clFlush(command_queue)); - cl_check(clFinish(command_queue)); - cl_check(clReleaseCommandQueue(command_queue)); - cl_check(clReleaseKernel(kernel)); - cl_check(clReleaseProgram(program)); - cl_check(clReleaseMemObject(d_a)); - cl_check(clReleaseContext(context)); - - free(a); + printf("Testing done!\n"); return 0; } diff --git a/src/demo_functions.c b/src/demo_functions.c deleted file mode 100644 index b67eac5..0000000 --- a/src/demo_functions.c +++ /dev/null @@ -1,86 +0,0 @@ -/*-------------demo_functions.c-----------------------------------------------// - -Purpose: This file defines a few functions to create an OpenCL kernel by - reading in a set of functions with the `define_functions` function. - - Notes: `define functions` should be made to read functions in at runtime -//----------------------------------------------------------------------------*/ -#include "../include/macros.h" -#include "../include/demo_functions.h" - -void define_add(char *final_fx){ - const char *kernel_str = "\n" \ - "float add(float a, float b){ \n" \ - " return a + b; \n" \ - "} \n"; - strcat(final_fx, kernel_str); -} -float add(float a, float b){ - return a + b; -} - -void define_preamble(char *final_fx){ - define_add(final_fx); - const char *kernel_str = "\n" \ - "__kernel void demo(__global float *a, int N){ \n" \ - " int idx = get_global_id(0); \n" \ - " int x = a[idx]; \n"; - strcat(final_fx, kernel_str); -} - -void define_postamble(char *final_fx){ - const char *kernel_str = "\n" \ - " a[idx] = x; \n" \ - "} \n"; - strcat(final_fx, kernel_str); -} - -void define_functions(char *final_fx, int n){ - char *all_fxs = (char*)malloc(MAX_SOURCE_SIZE); - - char f_1[50] = " x = add(x, 1);\n"; - char f_2[50] = " x += 2;\n"; - char f_3[50] = " x += 3;\n"; - char f_4[50] = " x += 4;\n"; - char f_5[50] = " x += 5;\n"; - char f_6[50] = " x += 6;\n"; - char f_7[50] = " x += 7;\n"; - char f_8[50] = " x += 8;\n"; - char f_9[50] = " x += 9;\n"; - char f_10[50] = " x += 10;\n"; - char f_11[50] = " x += 11;\n"; - char f_12[50] = " x += 12;\n"; - char f_13[50] = " x += 13;\n"; - char f_14[50] = " x += 14;\n"; - char f_15[50] = " x += 15;\n"; - char f_16[50] = " x += 16;\n"; - char f_17[50] = " x += 17;\n"; - char f_18[50] = " x += 18;\n"; - char f_19[50] = " x += 19;\n"; - char f_20[50] = " x += 20;\n"; - - for (int i = 0; i < n; ++i){ - strcat(all_fxs, f_1); - strcat(all_fxs, f_2); - strcat(all_fxs, f_3); - strcat(all_fxs, f_4); - strcat(all_fxs, f_5); - strcat(all_fxs, f_6); - strcat(all_fxs, f_7); - strcat(all_fxs, f_8); - strcat(all_fxs, f_9); - strcat(all_fxs, f_10); - strcat(all_fxs, f_11); - strcat(all_fxs, f_12); - strcat(all_fxs, f_13); - strcat(all_fxs, f_14); - strcat(all_fxs, f_15); - strcat(all_fxs, f_16); - strcat(all_fxs, f_17); - strcat(all_fxs, f_18); - strcat(all_fxs, f_19); - strcat(all_fxs, f_20); - } - - strcat(final_fx, all_fxs); -} diff --git a/src/errors.c b/src/errors.c index 7d96a6d..cca38db 100644 --- a/src/errors.c +++ b/src/errors.c @@ -305,4 +305,5 @@ const char *get_cl_error_string(cl_int error) */ default: return "Unspecified OpenCL error..."; } + return CL_SUCCESS; } diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..6fbe056 --- /dev/null +++ b/src/io.c @@ -0,0 +1,283 @@ +/*-------------io.c-----------------------------------------------------------// + +Purpose: This file is meant to define most of what is needed for + image and video creation + +//----------------------------------------------------------------------------*/ + +#include "../include/io.h" + +quibble_pixel qb_zero_pixel(void){ + quibble_pixel qp; + qp.red = 0; + qp.green = 0; + qp.blue = 0; + qp.alpha = 0; + return qp; +} + +quibble_pixel *qb_create_pixel_array(int height, int width){ + quibble_pixel *qpa = + (quibble_pixel *)malloc(sizeof(quibble_pixel)*width*height); + + for (int i = 0; i < width * height; ++i){ + qpa[i] = qb_zero_pixel(); + } + return qpa; +} + +size_t qb_find_type_size(char *type){ + + if (type == NULL){ + return 4; + } + + if (strcmp(type, "long double") == 0){ + return sizeof(long double); + } + + if (qb_find_next_char(type, 0, '*') >= 0 || + qb_find_next_string(type, 0, "long long") >= 0 || + qb_find_next_string(type, 0, "double") >= 0 || + qb_find_next_string(type, 0, "cl_mem") >= 0){ + return 8; + } + + if (qb_find_next_string(type, 0, "long") >= 0){ + return sizeof(long); + } + + if (qb_find_next_string(type, 0, "short") >= 0){ + return 2; + } + + if (qb_find_next_string(type, 0, "char") >= 0 || + qb_find_next_string(type, 0, "bool") >= 0){ + return 1; + } + + if (qb_find_next_string(type, 0, "int") >= 0 || + qb_find_next_string(type, 0, "signed") >= 0 || + qb_find_next_string(type, 0, "unsigned") >= 0 || + qb_find_next_string(type, 0, "float") >= 0){ + return 4; + } + + printf("Warning: type %s not found! Defaulting to int...\n", type); + return 4; +} + +char *qb_config_file(char *path){ + char *ret = (char *)calloc(256, sizeof(char)); + if (getenv("XDG_CONFIG_HOME") == NULL){ + strcat(ret, getenv("HOME")); + strcat(ret, "/.config/quibble/"); + } + else { + strcat(ret, getenv("XDG_CONFIG_HOME")); + strcat(ret, "/"); + } + strcat(ret, path); + + return ret; +} + +/*----------------------------------------------------------------------------// +FILE IO WORK +//----------------------------------------------------------------------------*/ + +bool qb_is_space(char a){ + if (a == ' ' || a == '\t' || a == '\n'){ + return true; + } + return false; +} + +// Necessary to convert QBINLINE **const** char * values to mutable char *'s +char *qb_copy(char *buffer){ + int n = strlen(buffer)+1; + char *copy_buffer = (char *)calloc(n, sizeof(char)); + strcpy(copy_buffer, buffer); + return copy_buffer; +} + +char *qb_strip_spaces(char *body, int start_index, int end_index){ + + char *final_str = NULL; + char curr_char; + if (start_index == end_index){ + curr_char = body[start_index]; + if (!qb_is_space(curr_char)){ + char *tmp_str = (char *)calloc(2, sizeof(char)); + tmp_str[0] = body[start_index]; + final_str = tmp_str; + return final_str; + } + else { + return NULL; + } + } + + int start_offset = 0; + int end_offset = 0; + + // Finding start offset + curr_char = body[start_index]; + while (qb_is_space(curr_char) && + start_offset + start_index < end_index){ + ++start_offset; + curr_char = body[start_index+start_offset]; + } + + // Finding end offset + curr_char = body[end_index]; + while (qb_is_space(curr_char) && + end_index - end_offset > start_index){ + ++end_offset; + curr_char = body[end_index - end_offset]; + } + + int str_len = (end_index - end_offset) - (start_index + start_offset)+1; + if (str_len > 0){ + final_str = (char *)calloc(str_len+1, sizeof(char)); + strncpy(final_str, &body[start_index+start_offset], str_len); + } + + return final_str; +} + +int qb_find_next_char(char *body, int current_index, char a){ + if (body == NULL){ + return -1; + } + + char *ptr = strchr(&body[current_index], a); + if (ptr != NULL){ + return ptr - body; + } + + return -1; +} + +int qb_find_next_string(char *body, int current_index, char *a){ + if (body == NULL){ + return -1; + } + + char *ptr = strstr(&body[current_index], a); + if (ptr != NULL){ + return ptr - body; + } + + return -1; +} + +int qb_find_matching_char(char *body, int body_size, int current_index, + char a, char b){ + + // Checking initial character to make sure it is valid + int open_count = -1; + if (body[current_index] == a){ + open_count = 1; + } + else{ + return -1; + } + + // Iterating through to find closing brackets + int i = current_index; + while (open_count > 0){ + ++i; + if (body[i] == a){ + ++open_count; + } + if (body[i] == b){ + --open_count; + } + if (i == body_size){ + return -1; + } + } + return i; +} + +int qb_find_occurrences(char *query, char *body){ + + + char *temp_str = strstr(body, query); + if (temp_str == NULL){ + return 0; + } + + int count = 0; + int query_length = strlen(query); + + while (temp_str != NULL){ + count++; + temp_str += query_length; + temp_str = strstr(temp_str, query); + } + + return count; +} + +/*----------------------------------------------------------------------------// + QBINLINE +//----------------------------------------------------------------------------*/ + +// replaces a character a with b if after char *prologue +void qb_replace_char_if_proceeding(char *content, char *query, char a, char b){ + + char *substr = strstr(content, query); + if (substr != NULL){ + qb_replace_next_char(substr, 0, a, b); + } +} + +void qb_replace_char(char *verse, int verse_size, char a, char b){ + + for (int i = 0; i < verse_size; ++i){ + if (verse[i] == a){ + verse[i] = b; + } + } +} + +void qb_replace_next_char(char *content, int index, char a, char b){ + char *tmp = strchr(&content[index], a); + content[tmp-content] = b; +} + +void qb_preprocess_content(char *content){ + if (qb_is_inlined(content)){ + int content_size = strlen(content); + // replace all spaces by new lines + qb_replace_char(content, content_size, ' ', '\n'); + + // except for the arguments after some preprocessor options + // that need to be in the same line + qb_replace_char_if_proceeding(content, "#ifdef", '\n', ' '); + qb_replace_char_if_proceeding(content, "#ifndef", '\n', ' '); + + // #define with two arguments will not work + qb_replace_char_if_proceeding(content, "#define", '\n', ' '); + + // don't leave any spaces in arguments + qb_replace_char_if_proceeding(content, "#if", '\n', ' '); + + // don't leave any spaces in arguments + qb_replace_char_if_proceeding(content, "#elif", '\n', ' '); + qb_replace_char_if_proceeding(content, "#pragma", '\n', ' '); + } +} + +bool qb_is_inlined(char *verse){ + char substr[23] = "// QBINLINE GENERATED\n"; + for (int i = 0; i < 22; ++i){ + if (verse[i] != substr[i]){ + return false; + } + } + + return true; +} diff --git a/src/qbinfo.c b/src/qbinfo.c new file mode 100644 index 0000000..f37f460 --- /dev/null +++ b/src/qbinfo.c @@ -0,0 +1,84 @@ +/*-------------qbinfo.c-------------------------------------------------------// + +Purpose: qb info is just an easy way to create and set OpenCL variables + +//----------------------------------------------------------------------------*/ + +#include "../include/qbinfo.h" + +char *get_device_name(cl_device_id device_id){ + size_t size; + clGetDeviceInfo(device_id, CL_DEVICE_NAME, 0, NULL, &size); + char *str = (char *)malloc(size); + clGetDeviceInfo(device_id, CL_DEVICE_NAME, size, str, NULL); + return str; +} + +char *get_platform_name(cl_platform_id platform_id){ + size_t size; + clGetPlatformInfo(platform_id, CL_PLATFORM_NAME, 0, NULL, &size); + char *str = (char *)malloc(size); + clGetPlatformInfo(platform_id, CL_PLATFORM_NAME, size, str, NULL); + return str; +} + +void qb_list_devices(void){ + + cl_platform_id *platform_ids; + cl_uint num_platforms; + + // Platforms + clGetPlatformIDs(10, NULL, &num_platforms); + cl_check( + clGetPlatformIDs(10, NULL, &num_platforms) + ); + + platform_ids = (cl_platform_id *)malloc(num_platforms * + sizeof(cl_platform_id)); + cl_check( + clGetPlatformIDs(num_platforms, platform_ids, NULL) + ); + + cl_uint num_devices; + for (size_t i = 0; i < num_platforms; ++i){ + char *platform_name = get_platform_name(platform_ids[i]); + + // As in `qb_find_platforms` and `qb_list devices`, 10 is arbitrary + cl_check( + clGetDeviceIDs(platform_ids[i], + CL_DEVICE_TYPE_ALL, + 0, + NULL, + &num_devices) + ); + + cl_device_id *device_ids = (cl_device_id *)malloc(num_devices * + sizeof(cl_device_id)); + cl_check( + clGetDeviceIDs(platform_ids[i], + CL_DEVICE_TYPE_ALL, + num_devices, + device_ids, + &num_devices) + ); + + printf("Platform %lu: %s. %d known device(s)...\n", i, platform_name, + num_devices); + + for (size_t j = 0; j < num_devices; ++j){ + char *device_name = get_device_name(device_ids[j]); + printf("\tDevice %lu: %s\n", j, device_name); + free(device_name); + } + free(platform_name); + free(device_ids); + } + free(platform_ids); +} + +int main(void){ + qb_list_devices(); + + return 0; +} + diff --git a/src/quibble_args.c b/src/quibble_args.c new file mode 100644 index 0000000..8bf6729 --- /dev/null +++ b/src/quibble_args.c @@ -0,0 +1,390 @@ +/*-------------args-----------------------------------------------------------// + + Purpose: To define all functions to deal with string -> args and prologue gen + +//----------------------------------------------------------------------------*/ + +#include "../include/quibble_args.h" + +void qb_find_type_arg(char *arg, char **type, char **variable){ + int len = strlen(arg); + bool iterate = true; + int index = len-1; + + while (iterate){ + if (arg[index] == '*' || qb_is_space(arg[index])){ + iterate = false; + } + index -= 1; + if (index < 0){ + iterate = false; + } + } + + // `index` should be just before the last character of the type or -1 + if (index < 0){ + type[0] = NULL; + char *tmp_variable = (char *)malloc((strlen(arg)+1)*sizeof(char)); + strcpy(tmp_variable, arg); + variable[0] = tmp_variable; + } + else { + index++; + type[0] = qb_strip_spaces(arg, 0, index); + variable[0] = qb_strip_spaces(arg, index+1, len); + } + +} + +void qb_parse_arg(quibble_arg *qa, char *arg){ + qb_find_type_arg(arg, &qa->type, &qa->variable); + free(arg); + +} + +void qb_parse_kwarg(quibble_kwarg *qk, char *lhs, char *rhs){ + + qb_find_type_arg(lhs, &qk->type, &qk->variable); + free(lhs); + + qk->value = rhs; +} + + +int qb_find_number_of_args(char *config){ + + int config_size = strlen(config); + int i = 0; + int num_entries = 0; + int pipe_loc = qb_find_next_char(config, i, '|'); + if (pipe_loc > 0){ + config_size = pipe_loc-1; + } + + if (config_size == 0){ + if (qb_is_space(config[i])){ + return 0; + } + else { + return 1; + } + } + + // only kwargs, no args... + if (qb_find_next_char(config, i, '=') <= config_size && + qb_find_next_char(config, i, '=') > 0){ + return 0; + } + + int next_comma = qb_find_next_char(config, i, ','); + + // Only 1 or 0 entries... + if (next_comma < 0){ + char *value = qb_strip_spaces(config, i, config_size); + int ret = 1; + if (value == NULL){ + return 0; + } + free(value); + return ret; + } + while (next_comma > 0){ + + if (next_comma > i){ + ++num_entries; + i = next_comma+1; + } + next_comma = qb_find_next_char(config, i, ','); + } + + return num_entries+1; +} + + +int qb_find_number_of_kwargs(char *config){ + + int i = 0; + int num_entries = 0; + + if (qb_find_next_char(config, i, '=') < 0){ + return 0; + } + + if (qb_find_next_char(config, i, '|') > 0){ + i += qb_find_next_char(config, i, '|') + 1; + } + if (qb_find_next_char(config, i, ',') > 0){ + fprintf(stderr, "Comma (,) used instead of semicolon (;)!\nEach quibble kwarg is a self-contained C expression and must end with a `;`!\n"); + exit(1); + } + if (qb_find_next_char(config, i, '=') > 0 && + qb_find_next_char(config, i, ';') < 0){ + fprintf(stderr, "Equal (=) found without terminating semicolon!\nEach quibble kwarg is a self-contained C expression and must end with a `;`!\n"); + exit(1); + } + + int next_equal = qb_find_next_char(config, i, '='); + int next_semicolon = qb_find_next_char(config, i, ';'); + + if(next_semicolon < 0){ + return 0; + } + while (next_semicolon > 0){ + + if (next_equal > i && next_semicolon > next_equal){ + ++num_entries; + i = next_semicolon + 1; + } + else { + fprintf(stderr, "Equal (=) found without terminating semicolon!\nEach quibble kwarg is a self-contained C expression and must end with a `;`!\n"); + exit(1); + } + next_equal = qb_find_next_char(config, i, '='); + next_semicolon = qb_find_next_char(config, i, ';'); + } + + return num_entries; + +} + +int qb_find_arg_index(quibble_arg *qa, int n, char *variable){ + if (n <= 0){ + return -1; + } + + + for (int i = 0; i < n; ++i){ + if (strcmp(qa[i].variable, variable) == 0){ + return i; + } + } + fprintf(stderr, "Argument %s not found!\n", variable); + exit(1); +} + +int qb_find_kwarg_index(quibble_kwarg *qk, int n, char *variable){ + if (n <= 0){ + return -1; + } + + for (int i = 0; i < n; ++i){ + if (strcmp(qk[i].variable, variable) == 0){ + return i; + } + } + fprintf(stderr, "Keyword variable %s not found!\n", variable); + exit(1); +} + +quibble_arg *qb_parse_args(char *config, int num_entries){ + + int config_size = strlen(config); + + if (num_entries > 0){ + quibble_arg *final_args = + (quibble_arg *)malloc(sizeof(quibble_arg) * num_entries); + + int i = 0; + int curr_entry = 0; + int next_comma = 0; + if (qb_find_next_char(config, i, '|') > 0){ + config_size = qb_find_next_char(config, i, '|'); + } + + while (i < config_size){ + next_comma = qb_find_next_char(config, i, ','); + + if (next_comma < 0){ + next_comma = config_size; + } + qb_parse_arg(&final_args[curr_entry], + qb_strip_spaces(config, i, next_comma-1)); + + // Check to make sure entry is unique + for (int j = 1; j <= curr_entry; ++j){ + if (strcmp(final_args[j-1].variable, + final_args[j].variable) == 0){ + fprintf(stderr, "Variable %s used more than once for arguments!\n", final_args[j].variable); + exit(1); + } + } + + i = next_comma + 1; + ++curr_entry; + } + + return final_args; + } + else { + return NULL; + } + +} + +quibble_kwarg *qb_parse_kwargs(char *config, int num_entries){ + + int config_size = strlen(config); + + if (num_entries > 0){ + quibble_kwarg *final_kwargs = + (quibble_kwarg *)malloc(sizeof(quibble_kwarg) * num_entries); + + int i = 0; + int curr_entry = 0; + int next_equal = 0; + int next_semicolon = 0; + if (qb_find_next_char(config, i, '|') > 0){ + i += qb_find_next_char(config, i, '|') + 1; + } + + while (i < config_size){ + next_equal = qb_find_next_char(config, i, '='); + next_semicolon = qb_find_next_char(config, i, ';'); + + qb_parse_kwarg(&final_kwargs[curr_entry], + qb_strip_spaces(config, i, next_equal-1), + qb_strip_spaces(config, + next_equal+1, + next_semicolon-1)); + + // Check to make sure entry is unique + for (int j = 1; j <= curr_entry; ++j){ + if (strcmp(final_kwargs[j-1].variable, + final_kwargs[j].variable) == 0){ + fprintf(stderr, "Variable %s used more than once for kwarg arguments!\n", final_kwargs[j].variable); + exit(1); + } + } + + i = next_semicolon + 1; + ++curr_entry; + + } + + return final_kwargs; + } + else { + return NULL; + } + +} + +// To be used in `qb_configure_verse` to create prologue string +char *qb_create_prologue(char *config, char *name, + quibble_arg *qargs, int num_args, + quibble_kwarg *qkwargs, int num_kwargs){ + + char *temp = (char *)calloc(MAX_PROLOGUE_SIZE, sizeof(char)); + + int num_config_args = qb_find_number_of_args(config); + + if (num_config_args == num_args){ + quibble_arg *temp_args = qb_parse_args(config, num_args); + for (int i = 0; i < num_args; ++i){ + if (qargs[i].type != NULL){ + strcat(temp, qargs[i].type); + } + strcat(temp, qargs[i].variable); + strcat(temp, " = "); + strcat(temp, temp_args[i].variable); + strcat(temp, ";\n"); + } + qb_free_arg_array(temp_args, num_args); + } + else { + fprintf(stderr, "Incorrect number of arguments for %s\nExpected %d, got %d!\n", name, num_args, num_config_args); + exit(1); + } + + int num_config_kwargs = qb_find_number_of_kwargs(config); + + if (num_config_kwargs > 0){ + quibble_kwarg *temp_kwargs = + qb_parse_kwargs(config, num_config_kwargs); + + char *value; + for (int i = 0; i < num_kwargs; ++i){ + + value = qkwargs[i].value; + for (int j = 0; j < num_config_kwargs; ++j){ + if (strcmp(temp_kwargs[j].variable, + qkwargs[i].variable) == 0){ + value = temp_kwargs[i].value; + } + } + + if (qkwargs[i].type != NULL){ + strcat(temp, qkwargs[i].type); + strcat(temp, " "); + } + strcat(temp, qkwargs[i].variable); + strcat(temp, " = "); + strcat(temp, value); + strcat(temp, ";\n"); + } + qb_free_kwarg_array(temp_kwargs, num_config_kwargs); + } + else { + for (int i = 0; i < num_kwargs; ++i){ + if (qkwargs[i].type != NULL){ + strcat(temp, qkwargs[i].type); + strcat(temp, " "); + } + strcat(temp, qkwargs[i].variable); + strcat(temp, " = "); + strcat(temp, qkwargs[i].value); + strcat(temp, ";\n"); + } + } + + int len = strlen(temp); + char *final_output = (char *)calloc(len+1, sizeof(char)); + + for (int i = 0; i < len; ++i){ + final_output[i] = temp[i]; + } + + free(temp); + return final_output; +} + +/*----------------------------------------------------------------------------// + FREE +//----------------------------------------------------------------------------*/ + +void qb_free_arg(quibble_arg qarg){ + free(qarg.type); + free(qarg.variable); +} + +void qb_free_kwarg(quibble_kwarg qkwarg){ + free(qkwarg.type); + free(qkwarg.variable); + free(qkwarg.value); +} + +void qb_free_arg_array(quibble_arg *qargs, int n){ + for (int i = 0; i < n; ++i){ + qb_free_arg(qargs[i]); + } + free(qargs); +} + +void qb_free_kwarg_array(quibble_kwarg *qkwargs, int n){ + for (int i = 0; i < n; ++i){ + qb_free_kwarg(qkwargs[i]); + } + free(qkwargs); +} + +/*----------------------------------------------------------------------------// + PRINTING +//----------------------------------------------------------------------------*/ + +void qb_print_arg(quibble_arg qa){ + printf("%s\n", qa.variable); +} + +void qb_print_kwarg(quibble_kwarg qk){ + printf("%s = %s\n", qk.variable, qk.value); +} diff --git a/src/quibble_poems.c b/src/quibble_poems.c new file mode 100644 index 0000000..851576f --- /dev/null +++ b/src/quibble_poems.c @@ -0,0 +1,288 @@ +/*-------------poem-----------------------------------------------------------// + + Purpose: A quibble poem is a user-submitted collection of stanzas and verses + +//----------------------------------------------------------------------------*/ + +#include "../include/quibble_program.h" + +bool qb_is_poem(char *poem, int offset){ + char substr[7] = "__poem"; + for (int i = 0; i < 6; ++i){ + if (poem[i+offset] != substr[i]){ + return false; + } + } + + return true; +} + +bool qb_find_keyword_in_poems(quibble_program qp, char *keyword){ + int found_index = -1; + for (int i = 0; i < qp.num_poems; ++i){ + found_index = qb_find_next_string(qp.poem_list[i].body, 0, keyword); + } + if (found_index >= 0){ + return true; + } + return false; + +} + +quibble_poem qb_parse_poem(char *poem){ + int poem_size = strlen(poem); + int offset = 0; + bool qbinlined = qb_is_inlined(poem); + if (qbinlined){ + offset += 22; + } + + if (qb_is_poem(poem, offset)){ + offset += 6; + } + else { + fprintf(stderr, "Quibble poems must be configured with `__poem`!\n"); + exit(1); + } + + quibble_poem final_poem; + + int config_start = qb_find_next_char(poem, offset, '(')+1; + int config_end = + qb_find_matching_char(poem, poem_size, config_start-1, '(', ')'); + + + if (config_end-config_start > 0){ + char *config = + (char *)calloc((config_end-config_start)+1, sizeof(char)); + + for (int i = 0; i < (config_end-config_start); ++i){ + config[i] = poem[config_start+i]; + } + + final_poem.num_args = qb_find_number_of_args(config); + final_poem.args = + qb_parse_args(config, final_poem.num_args); + + free(config); + } + else { + final_poem.args = NULL; + final_poem.num_args = 0; + } + + + int body_start = qb_find_next_char(poem, offset, '{')+1; + int body_end = qb_find_matching_char(poem, poem_size, body_start-1, '{', '}'); + + if (body_end-body_start > 0){ + char *body = (char *)calloc((body_end-body_start)+2, sizeof(char)); + for (int i = 0; i < (body_end-body_start); ++i){ + body[i] = poem[body_start+i]; + } + + if (qbinlined){ + qb_preprocess_content(body); + } + final_poem.body = body; + } + else{ + final_poem.body = NULL; + } + final_poem.name = qb_strip_spaces(poem, offset, config_start-2); + + return final_poem; +} + +// TODO: ADD OTHERS: _pt, _time, _world_size, _origin ... +char *qb_expand_poem(quibble_program qp, int poem_index){ + + bool do_expansion = true; + + if (qp.poem_list[poem_index].body == NULL){ + do_expansion = false; + } + char *tmp_body = (char *)calloc(MAX_META_SIZE, sizeof(char)); + char *body = qp.poem_list[poem_index].body; + + strcat(tmp_body, "__kernel void "); + strcat(tmp_body, qp.poem_list[poem_index].name); + strcat(tmp_body, "("); + if (qp.poem_list[poem_index].num_args > 0){ + //strcat(tmp_body, ", "); + for (int i = 0; i < qp.poem_list[poem_index].num_args; ++i){ + + if (qp.poem_list[poem_index].args[i].type != NULL){ + strcat(tmp_body, qp.poem_list[poem_index].args[i].type); + strcat(tmp_body, " "); + } + strcat(tmp_body, qp.poem_list[poem_index].args[i].variable); + if (i == qp.poem_list[poem_index].num_args - 1){ + strcat(tmp_body, "){\n"); + } + else{ + strcat(tmp_body, ", "); + } + } + } + else { + strcat(tmp_body, "){\n"); + } + + if (do_expansion){ + if (qb_find_keyword(qp, "_idx")){ + strcat(tmp_body, " int _idx = get_global_id(0);\n"); + } + + // Parse verse content + int index = 0; + int tmp_index = strlen(tmp_body); + int max_size = strlen(body); + int vcall_match_count = 0; + int scall_match_count = 0; + + int meta_name_start; + int meta_name_end; + int config_end; + int meta_length; + + int stanza_body_start; + int stanza_body_end; + + char *vcall_string = "@VCALL "; + char *scall_string = "@SCALL "; + while (index < max_size){ + if (body[index] == vcall_string[vcall_match_count]){ + vcall_match_count++; + if (vcall_match_count == 7){ + + tmp_index -= 6; + memset(tmp_body+tmp_index, 0, strlen(tmp_body)); + + meta_name_start = index + 1; + meta_name_end = qb_find_next_char(body, meta_name_start, '('); + char *verse_name = qb_strip_spaces(body, meta_name_start, + meta_name_end - 1); + config_end = qb_find_matching_char(body, max_size, + meta_name_end, '(',')')-1; + + if (config_end > meta_name_end+1){ + char *config = (char *)calloc(config_end-meta_name_end+2, + sizeof(char)); + for (int i = 0; i < config_end-meta_name_end; ++i){ + config[i] = body[meta_name_end+1+i]; + } + char *verse = qb_expand_verse(qp, verse_name, config); + meta_length = strlen(verse); + for (int i = 0; i < meta_length; ++i){ + tmp_body[tmp_index + i] = verse[i]; + } + tmp_index += meta_length; + index = config_end + 2; + if (body[index] == ';'){ + index++; + } + free(verse); + free(config); + } + else{ + fprintf(stderr, "Verse %s called incorrectly!\n", verse_name); + exit(1); + } + free(verse_name); + } + } + else if (vcall_match_count != 0){ + vcall_match_count = 0; + } + + if (body[index] == scall_string[scall_match_count]){ + scall_match_count++; + if (scall_match_count == 7){ + tmp_index -= 6; + memset(tmp_body+tmp_index, 0, 7); + + meta_name_start = index + 1; + meta_name_end = qb_find_next_char(body, meta_name_start, '('); + char *stanza_name = qb_strip_spaces(body, meta_name_start, + meta_name_end - 1); + config_end = qb_find_matching_char(body, max_size, + meta_name_end, '(',')')-1; + + stanza_body_start = qb_find_next_char(body, config_end, '{')+1; + stanza_body_end = qb_find_matching_char(body, max_size, + stanza_body_start-1, '{','}'); + if (config_end > meta_name_end+1 && + stanza_body_end > stanza_body_start){ + char *config = (char *)calloc(config_end-meta_name_end+2, + sizeof(char)); + + for (int i = 0; i < config_end-meta_name_end; ++i){ + config[i] = body[meta_name_end+1+i]; + } + char *stanza_body = (char *)calloc(stanza_body_end - + stanza_body_start+2, sizeof(char)); + for (int i = 0; + i < stanza_body_end - stanza_body_start; + ++i){ + stanza_body[i] = body[stanza_body_start+i]; + } + char *stanza = qb_expand_stanza(qp, stanza_name, config, + stanza_body); + meta_length = strlen(stanza); + for (int i = 0; i < meta_length; ++i){ + tmp_body[tmp_index + i] = stanza[i]; + } + tmp_index += meta_length; + index = stanza_body_end + 1; + free(stanza); + free(config); + free(stanza_body); + } + else{ + fprintf(stderr, "Stanza %s called incorrectly!\n", + stanza_name); + exit(1); + } + free(stanza_name); + } + } + else if (scall_match_count != 0){ + scall_match_count = 0; + } + + + tmp_body[tmp_index] = body[index]; + ++index; + ++tmp_index; + } + } + + + strcat(tmp_body, "\n}\n"); + int len = strlen(tmp_body); + char *final_body = (char *)calloc(len+2, sizeof(char)); + + for (int i = 0; i < len; ++i){ + final_body[i] = tmp_body[i]; + } + + free(tmp_body); + return final_body; + +} + +void qb_free_poem(quibble_poem qp){ + free(qp.body); + free(qp.name); + qb_free_arg_array(qp.args, qp.num_args); +} + +void qb_print_poem(quibble_poem qp){ + printf("Quibble Poem:\nName: %s\nBody:\n%s\n", qp.name, qp.body); + printf("Args...\n"); + for (int i = 0; i < qp.num_args; ++i){ + qb_print_arg(qp.args[i]); + } + printf("\n"); +} diff --git a/src/quibble_program.c b/src/quibble_program.c new file mode 100644 index 0000000..7dec21d --- /dev/null +++ b/src/quibble_program.c @@ -0,0 +1,626 @@ +/*-------------program--------------------------------------------------------// + + Purpose: A quibble program is a user-submitted set of verses, stanzas, poems, + and functions (usually from a *.qbl file) + + Notes: Some unnecessary code duplication here, but... meh + Make sure to allocate NULL when num_anything == 0 + +//----------------------------------------------------------------------------*/ + +#include "../include/quibble_program.h" + +/*----------------------------------------------------------------------------// + PROGRAM GENERATION +//----------------------------------------------------------------------------*/ +// Reads an input file and parses everything into verses or OCL functions +quibble_program qb_parse_program_file(char *filename){ + + // Opening file + FILE* fileptr; + fileptr = fopen(filename, "r"); + + if (fileptr == NULL){ + fprintf(stderr, "Error opening file %s!\n", filename); + exit(1); + } + + // Finding file length + fseek(fileptr, 0, SEEK_END); + int filesize = ftell(fileptr); + fseek(fileptr, 0, SEEK_SET); + + // Creating initial buffer + char *buffer = (char *)calloc(filesize+1, sizeof(char)); + + while (!feof(fileptr)){ + fread(buffer, sizeof(char), filesize, fileptr); + } + + fclose(fileptr); + quibble_program qp = qb_parse_program(buffer); + + return qp; +} + +quibble_program qb_parse_program(char *program){ + + int num_verses = qb_find_occurrences("__verse", program); + int num_stanzas = qb_find_occurrences("__stanza", program); + int num_poems = qb_find_occurrences("__poem", program); + + int filesize = strlen(program); + + // creating program and populating verses and everything else + quibble_program qp; + qp.configured = false; + + qp.num_verses = num_verses; + qp.num_stanzas = num_stanzas; + qp.num_poems = num_poems; + + if (num_verses > 0){ + qp.verse_list = + (quibble_verse *)malloc(sizeof(quibble_verse)*num_verses); + } + else { + qp.verse_list = NULL; + } + + if (num_stanzas > 0){ + qp.stanza_list = + (quibble_stanza *)malloc(sizeof(quibble_stanza)*num_stanzas); + } + else { + qp.stanza_list = NULL; + } + + if (num_poems > 0){ + qp.poem_list = (quibble_poem *)malloc(sizeof(quibble_poem)*num_poems); + } + else { + qp.poem_list = NULL; + } + + if (num_verses > 0 || + num_stanzas > 0 || + num_poems > 0){ + char *tmp_everything_else = + (char *)calloc(MAX_SOURCE_SIZE, sizeof(char)); + + char *tmp_verse = NULL; + if (num_verses > 0){ + tmp_verse = (char *)calloc(MAX_SOURCE_SIZE, sizeof(char)); + } + + char *tmp_poem = NULL; + if (num_poems > 0){ + tmp_poem = (char *)calloc(MAX_SOURCE_SIZE, sizeof(char)); + } + + char *tmp_stanza = NULL; + if (num_stanzas > 0){ + tmp_stanza = (char *)calloc(MAX_SOURCE_SIZE, sizeof(char)); + } + + int index = 0; + int everything_else_index = 0; + + bool qbinlined = qb_is_inlined(program); + char *buffer = program; + if (qbinlined){ + index += 22; + buffer = qb_copy(buffer); + qb_preprocess_content(buffer); + } + + int verse_match_count = 0; + int stanza_match_count = 0; + int poem_match_count = 0; + + int curr_verse = 0; + int curr_poem = 0; + int curr_stanza = 0; + + int verse_start = 0; + int poem_start = 0; + int stanza_start = 0; + + int verse_end = 0; + int poem_end = 0; + int stanza_end = 0; + + char *verse_string = "__verse"; + char *stanza_string = "__stanza"; + char *poem_string = "__poem"; + + while (index < filesize){ + if (buffer[index] == poem_string[poem_match_count] && + curr_poem < num_poems){ + ++poem_match_count; + if (poem_match_count == 6){ + poem_start = index - 5; + poem_end = qb_find_next_char(buffer, poem_start, '('); + poem_end = qb_find_matching_char( + buffer, filesize, poem_end, '(', ')'); + + poem_end = qb_find_next_char(buffer, poem_end, '{'); + poem_end = qb_find_matching_char( + buffer, filesize, poem_end, '{', '}') + 1; + + for (int j = 0; j < poem_end - poem_start; ++j){ + tmp_poem[j] = buffer[poem_start + j]; + } + + qp.poem_list[curr_poem] = qb_parse_poem(tmp_poem); + ++curr_poem; + + // setting everything back + index = poem_end; + poem_match_count = 0; + everything_else_index -= 5; + memset(tmp_everything_else+everything_else_index, 0, + 5*sizeof(char)); + poem_end = 0; + poem_start = 0; + memset(tmp_poem,0,strlen(tmp_poem)); + } + } + else if (poem_match_count != 0){ + poem_match_count = 0; + } + + if (buffer[index] == stanza_string[stanza_match_count] && + curr_stanza < num_stanzas){ + ++stanza_match_count; + if (stanza_match_count == 8){ + stanza_start = index - 7; + stanza_end = qb_find_next_char(buffer, stanza_start, '('); + stanza_end = qb_find_matching_char( + buffer, filesize, stanza_end, '(', ')'); + + stanza_end = qb_find_next_char(buffer, stanza_end, '{'); + stanza_end = qb_find_matching_char( + buffer, filesize, stanza_end, '{', '}') + 1; + + for (int j = 0; j < stanza_end - stanza_start; ++j){ + tmp_stanza[j] = buffer[stanza_start + j]; + } + + qp.stanza_list[curr_stanza] = qb_parse_stanza(tmp_stanza); + ++curr_stanza; + + // setting everything back + index = stanza_end; + stanza_match_count = 0; + everything_else_index -= 7; + memset(tmp_everything_else+everything_else_index, 0, + 7*sizeof(char)); + stanza_end = 0; + stanza_start = 0; + memset(tmp_stanza,0,strlen(tmp_stanza)); + } + } + else if (stanza_match_count != 0){ + stanza_match_count = 0; + } + + if (buffer[index] == verse_string[verse_match_count] && + curr_verse < num_verses){ + ++verse_match_count; + if (verse_match_count == 7){ + verse_start = index - 6; + verse_end = qb_find_next_char(buffer, verse_start, '('); + verse_end = qb_find_matching_char( + buffer, filesize, verse_end, '(', ')'); + + verse_end = qb_find_next_char(buffer, verse_end, '{'); + verse_end = qb_find_matching_char( + buffer, filesize, verse_end, '{', '}') + 1; + + for (int j = 0; j < verse_end - verse_start; ++j){ + tmp_verse[j] = buffer[verse_start + j]; + } + + qp.verse_list[curr_verse] = qb_parse_verse(tmp_verse); + ++curr_verse; + + // setting everything back + index = verse_end; + verse_match_count = 0; + everything_else_index -= 6; + memset(tmp_everything_else+everything_else_index, 0, + 6*sizeof(char)); + verse_end = 0; + verse_start = 0; + memset(tmp_verse,0,strlen(tmp_verse)); + } + } + else if (verse_match_count != 0){ + verse_match_count = 0; + } + + tmp_everything_else[everything_else_index] = buffer[index]; + ++index; + ++everything_else_index; + } + + char *everything_else = + qb_strip_spaces(tmp_everything_else, + 0, strlen(tmp_everything_else)-1); + + qp.everything_else = everything_else; + + // Only free if we are not using it directly + free(buffer); + free(tmp_everything_else); + free(tmp_verse); + free(tmp_stanza); + free(tmp_poem); + } + else { + qp.everything_else = program; + } + + qb_build_program(&qp); + + + return qp; +} + +quibble_verse qb_find_verse(quibble_program qp, char *verse_name){ + + for (int i = 0; i < qp.num_verses; ++i){ + if (strcmp(qp.verse_list[i].name, verse_name) == 0){ + return qp.verse_list[i]; + } + } + fprintf(stderr, "No verse %s found!\n", verse_name); + exit(1); +} + +quibble_stanza qb_find_stanza(quibble_program qp, char *stanza_name){ + + for (int i = 0; i < qp.num_stanzas; ++i){ + if (strcmp(qp.stanza_list[i].name, stanza_name) == 0){ + return qp.stanza_list[i]; + } + } + fprintf(stderr, "No stanza %s found!\n", stanza_name); + exit(1); +} + +quibble_poem qb_find_poem(quibble_program qp, char *poem_name){ + + for (int i = 0; i < qp.num_poems; ++i){ + if (strcmp(qp.poem_list[i].name, poem_name) == 0){ + return qp.poem_list[i]; + } + } + fprintf(stderr, "No poem %s found!\n", poem_name); + exit(1); +} + +/*----------------------------------------------------------------------------// +CONFIGURATION +//----------------------------------------------------------------------------*/ + +int qb_find_verse_index(quibble_program qp, char *name){ + for (int i = 0; i < qp.num_verses; ++i){ + if (strcmp(qp.verse_list[i].name, name) == 0){ + return i; + } + } + fprintf(stderr, "Verse %s not found!\n", name); + exit(1); +} + +int qb_find_poem_index(quibble_program qp, char *name){ + for (int i = 0; i < qp.num_poems; ++i){ + if (strcmp(qp.poem_list[i].name, name) == 0){ + return i; + } + } + fprintf(stderr, "Poem %s not found!\n", name); + exit(1); +} + +int qb_find_stanza_index(quibble_program qp, char *name){ + for (int i = 0; i < qp.num_stanzas; ++i){ + if (strcmp(qp.stanza_list[i].name, name) == 0){ + return i; + } + } + fprintf(stderr, "Stanza %s not found!\n", name); + exit(1); +} + +void qb_rebuild_program(quibble_program *qp){ + free(qp->body); + qb_build_program(qp); +} + +void qb_build_program(quibble_program *qp){ + char *body = (char *)calloc(MAX_SOURCE_SIZE, sizeof(char)); + if (qp->everything_else != NULL){ + strcat(body, qp->everything_else); + strcat(body, "\n\n"); + } + + for (int i = 0; i < qp->num_poems; ++i){ + char *temp_poem = qb_expand_poem(qp[0], i); + if (temp_poem != NULL){ + strcat(body, temp_poem); + strcat(body, "\n"); + } + free(temp_poem); + } + + + int len = strlen(body)+1; + char *final_body = (char *)calloc(len, sizeof(char)); + for (int i = 0; i < len; ++i){ + final_body[i] = body[i]; + } + qp->body = final_body; + free(body); +} + +void qb_free_program(quibble_program qp){ + free(qp.everything_else); + free(qp.body); + for (int i = 0; i < qp.num_verses; ++i){ + qb_free_verse(qp.verse_list[i]); + } + for (int i = 0; i < qp.num_stanzas; ++i){ + qb_free_stanza(qp.stanza_list[i]); + } + for (int i = 0; i < qp.num_poems; ++i){ + qb_free_poem(qp.poem_list[i]); + } + free(qp.verse_list); + free(qp.stanza_list); + free(qp.poem_list); + + // OpenCL freeing + if (qp.configured){ + free(qp.platform_ids); + free(qp.device_ids); + cl_check(clFlush(qp.command_queue)); + cl_check(clFinish(qp.command_queue)); + cl_check(clReleaseCommandQueue(qp.command_queue)); + cl_check(clReleaseContext(qp.context)); + cl_check(clReleaseProgram(qp.program)); + + for (int i = 0; i < qp.num_poems; ++i){ + cl_check(clReleaseKernel(qp.kernels[i])); + } + free(qp.kernels); + } +} + +void qb_print_program(quibble_program qp){ + + printf("Quibble Program:\nBody\n%s\nEverything Else:\n%s\n\n", + qp.body, qp.everything_else); + + for (int i = 0; i < qp.num_poems; ++i){ + qb_print_poem(qp.poem_list[i]); + } + + for (int i = 0; i < qp.num_stanzas; ++i){ + qb_print_stanza(qp.stanza_list[i]); + } + + for (int i = 0; i < qp.num_verses; ++i){ + qb_print_verse(qp.verse_list[i]); + } + + if (qp.configured){ + print_cl_info(qp); + } +} + +/*----------------------------------------------------------------------------// + OpenCL Interface +//----------------------------------------------------------------------------*/ + +bool qb_find_keyword(quibble_program qp, char *keyword){ + + if(qb_find_keyword_in_poems(qp, keyword)){ + return true; + } + if(qb_find_keyword_in_stanzas(qp, keyword)){ + return true; + } + if(qb_find_keyword_in_verses(qp, keyword)){ + return true; + } + + return false; +} + +char *get_device_name(cl_device_id device_id){ + size_t size; + clGetDeviceInfo(device_id, CL_DEVICE_NAME, 0, NULL, &size); + char *str = (char *)malloc(size); + clGetDeviceInfo(device_id, CL_DEVICE_NAME, size, str, NULL); + return str; +} + +char *get_platform_name(cl_platform_id platform_id){ + size_t size; + clGetPlatformInfo(platform_id, CL_PLATFORM_NAME, 0, NULL, &size); + char *str = (char *)malloc(size); + clGetPlatformInfo(platform_id, CL_PLATFORM_NAME, size, str, NULL); + return str; +} + +void print_cl_info(quibble_program qp){ + char *platform_name = + get_platform_name(qp.platform_ids[qp.chosen_platform]); + char *device_name = get_device_name(qp.device_ids[qp.chosen_device]); + + printf("Platform: %s\nDevice: %s\n\n", platform_name, device_name); + free(platform_name); + free(device_name); +} + +void qb_find_platforms(quibble_program *qp){ + // 10 is arbitrary, but should be high enough for almost any task + // This finds &qp->num_platforms + cl_check( + clGetPlatformIDs(10, NULL, &qp->num_platforms) + ); + + qp->platform_ids = (cl_platform_id *)malloc(qp->num_platforms * + sizeof(cl_platform_id)); + + // This finds qp->platform_ids, + cl_check( + clGetPlatformIDs(qp->num_platforms, qp->platform_ids, NULL) + ); +} + +void qb_find_devices(quibble_program *qp){ + + // finds &qp->num_devices + cl_check( + clGetDeviceIDs(qp->platform_ids[qp->chosen_platform], + CL_DEVICE_TYPE_ALL, + 0, + NULL, + &qp->num_devices) + ); + + qp->device_ids = (cl_device_id *)malloc(qp->num_devices * + sizeof(cl_device_id)); + + // finds &qp->num_devices + cl_check( + clGetDeviceIDs(qp->platform_ids[qp->chosen_platform], + CL_DEVICE_TYPE_ALL, + qp->num_devices, + qp->device_ids, + &qp->num_devices) + ); + +} + +void qb_configure_program(quibble_program *qp, int platform, int device){ + qp->chosen_platform = platform; + qp->chosen_device = device; + + qb_find_platforms(qp); + qb_find_devices(qp); + + cl_int err; + + qp->context = clCreateContext(NULL, + 1, + &qp->device_ids[qp->chosen_device], + NULL, + NULL, + &err); + cl_check(err); + + qp->command_queue = clCreateCommandQueueWithProperties( + qp->context, + qp->device_ids[qp->chosen_device], + 0, + &err + ); + cl_check(err); + + // Create program + size_t kernel_size = strlen(qp->body); + qp->program = clCreateProgramWithSource( + qp->context, + 1, + (const char**)&qp->body, + (const size_t *)&kernel_size, + &err + ); + cl_check(err); + + err = clBuildProgram(qp->program, + 1, + &qp->device_ids[qp->chosen_device], + NULL, + NULL, + NULL); + cl_check_program(err, qp->program, + qp->device_ids[qp->chosen_device]); + + cl_kernel *kernels = (cl_kernel *)malloc(sizeof(cl_kernel)*qp->num_poems); + for (int i = 0; i < qp->num_poems; ++i){ + + kernels[i] = clCreateKernel(qp->program, qp->poem_list[i].name, &err); + cl_check(err); + } + + qp->kernels = kernels; + qp->configured = true; +} + +void qb_run(quibble_program qp, char *kernel_name, + size_t global_item_size, + size_t local_item_size){ + + int kernel_num = qb_find_poem_index(qp, kernel_name); + + cl_check( + clEnqueueNDRangeKernel(qp.command_queue, + qp.kernels[kernel_num], + 1, + NULL, + &global_item_size, + &local_item_size, + 0, + NULL, + NULL) + ); + +} + +void qb_set_arg(quibble_program *qp, char *poem, char *arg, void *data){ + int poem_index = qb_find_poem_index(*qp, poem); + + char *type; + char *variable; + + qb_find_type_arg(arg, &type, &variable); + + int arg_index = qb_find_arg_index(qp->poem_list[poem_index].args, + qp->poem_list[poem_index].num_args, + variable); + + size_t object_size = qb_find_type_size(type); + + cl_check( + clSetKernelArg(qp->kernels[poem_index], + arg_index, + object_size, + (void *)data) + ); + + free(type); + free(variable); +} + +void qb_set_args_variadic(quibble_program *qp, char *poem, int n, va_list args){ + for (int i = 0; i < n; ++i){ + char *curr_arg = va_arg(args, char *); + void *curr_data = va_arg(args, void *); + qb_set_arg(qp, poem, curr_arg, curr_data); + } +} + +void qb_set_args(quibble_program *qp, char *poem, int n, ...){ + va_list args; + va_start(args, 2*n); + + qb_set_args_variadic(qp, poem, n, args); + + va_end(args); +} diff --git a/src/quibble_stanzas.c b/src/quibble_stanzas.c new file mode 100644 index 0000000..1a5b92a --- /dev/null +++ b/src/quibble_stanzas.c @@ -0,0 +1,266 @@ +/*-------------stanza---------------------------------------------------------// + + Purpose: A quibble stanza is a user-defined method for composing verses + +//----------------------------------------------------------------------------*/ + +#include "../include/quibble_program.h" + +bool qb_is_stanza(char *stanza, int offset){ + char substr[9] = "__stanza"; + for (int i = 0; i < 8; ++i){ + if (stanza[i+offset] != substr[i]){ + return false; + } + } + + return true; +} + +bool qb_find_keyword_in_stanzas(quibble_program qp, char *keyword){ + int found_index = -1; + + for (int i = 0; i < qp.num_stanzas; ++i){ + found_index = qb_find_next_string(qp.stanza_list[i].prologue, + 0, + keyword); + found_index = qb_find_next_string(qp.stanza_list[i].epilogue, + 0, + keyword); + } + if (found_index >= 0){ + return true; + } + return false; +} + +quibble_stanza qb_parse_stanza(char *stanza){ + int stanza_size = strlen(stanza); + int offset = 0; + bool is_split = true; + bool qbinlined = qb_is_inlined(stanza); + if (qbinlined){ + offset += 22; + } + + if (qb_is_stanza(stanza, offset)){ + offset += 8; + } + else { + fprintf(stderr, "Quibble stanzas must be configured with `__stanza`!\n"); + exit(1); + } + + quibble_stanza final_stanza; + + int config_start = qb_find_next_char(stanza, offset, '(')+1; + int config_end = + qb_find_matching_char(stanza, stanza_size, config_start-1, '(', ')'); + + + if (config_end-config_start > 0){ + char *config = + (char *)calloc((config_end-config_start)+1, sizeof(char)); + + for (int i = 0; i < (config_end-config_start); ++i){ + config[i] = stanza[config_start+i]; + } + + final_stanza.num_args = qb_find_number_of_args(config); + final_stanza.args = + qb_parse_args(config, final_stanza.num_args); + + final_stanza.num_kwargs = qb_find_number_of_kwargs(config); + final_stanza.kwargs = + qb_parse_kwargs(config, final_stanza.num_kwargs); + free(config); + } + else { + final_stanza.args = NULL; + final_stanza.num_args = 0; + final_stanza.kwargs = NULL; + final_stanza.num_kwargs = 0; + } + + int prologue_start = qb_find_next_char(stanza, offset, '{')+1; + int prologue_end = + qb_find_next_string(stanza, prologue_start, "__split_stanza();"); + + // No split found + if (prologue_end < 0){ + is_split = false; + prologue_end = qb_find_matching_char(stanza, stanza_size, + prologue_start-1, '{', '}'); + } + + if (prologue_end-prologue_start > 0 && prologue_end > 0){ + char *prologue = (char *)calloc((prologue_end-prologue_start)+1, + sizeof(char)); + for (int i = 0; i < (prologue_end-prologue_start); ++i){ + prologue[i] = stanza[prologue_start+i]; + } + + if (qbinlined){ + qb_preprocess_content(prologue); + } + final_stanza.prologue = prologue; + } + else{ + final_stanza.prologue = NULL; + } + + if (is_split){ + int epilogue_start = prologue_end + 18; + int epilogue_end = qb_find_matching_char(stanza, stanza_size, + prologue_start-1, '{', '}'); + + if (epilogue_end-epilogue_start > 0){ + char *epilogue = (char *)calloc((epilogue_end-epilogue_start)+1, + sizeof(char)); + for (int i = 0; i < (epilogue_end-epilogue_start); ++i){ + epilogue[i] = stanza[epilogue_start+i]; + } + + if (qbinlined){ + qb_preprocess_content(epilogue); + } + final_stanza.epilogue = epilogue; + } + else{ + final_stanza.epilogue = NULL; + } + } + else{ + final_stanza.epilogue = NULL; + } + + final_stanza.name = qb_strip_spaces(stanza, offset, config_start-2); + + return final_stanza; +} + +char *qb_expand_stanza(quibble_program qp, + char* stanza_name, char *config, char *body){ + + int idx = qb_find_stanza_index(qp, stanza_name); + if (qp.stanza_list[idx].prologue == NULL){ + return NULL; + } + + char *tmp_prologue = qb_create_prologue(config, stanza_name, + qp.stanza_list[idx].args, qp.stanza_list[idx].num_args, + qp.stanza_list[idx].kwargs, qp.stanza_list[idx].num_kwargs); + + char *tmp_body = (char *)calloc(MAX_META_SIZE, sizeof(char)); + strcat(tmp_body, tmp_prologue); + strcat(tmp_body, qp.stanza_list[idx].prologue); + + // Parse verse content + int index = 0; + int tmp_index = strlen(tmp_body); + int max_size = strlen(body); + int vcall_match_count = 0; + + int verse_name_start; + int verse_name_end; + int config_end; + int verse_length; + char *vcall_string = "@VCALL "; + while (index < max_size){ + if (body[index] == vcall_string[vcall_match_count]){ + vcall_match_count++; + if (vcall_match_count == 7){ + tmp_index -= 6; + memset(tmp_body+tmp_index, 0, 7); + + verse_name_start = index; + verse_name_end = qb_find_next_char(body, verse_name_start, '('); + char *verse_name = qb_strip_spaces(body, verse_name_start, + verse_name_end - 1); + config_end = qb_find_matching_char(body, max_size, + verse_name_end, '(',')')-1; + + if (config_end > verse_name_end+1){ + char *config = (char *)calloc(config_end-verse_name_end+2, + sizeof(char)); + for (int i = 0; i < config_end-verse_name_end; ++i){ + config[i] = body[verse_name_end+1+i]; + } + char *verse = qb_expand_verse(qp, verse_name, config); + verse_length = strlen(verse); + for (int i = 0; i < verse_length; ++i){ + tmp_body[tmp_index + i] = verse[i]; + } + index = config_end+2; + if (body[index] == ';'){ + index++; + } + tmp_index += verse_length; + free(config); + free(verse); + } + else{ + fprintf(stderr, "Verse %s called incorrectly!\n", verse_name); + exit(1); + } + free(verse_name); + } + } + else if (vcall_match_count != 0){ + vcall_match_count = 0; + } + tmp_body[tmp_index] = body[index]; + ++index; + ++tmp_index; + } + + + if (qp.stanza_list[idx].epilogue != NULL){ + strcat(tmp_body, qp.stanza_list[idx].epilogue); + } + + int len = strlen(tmp_body); + char *final_body = (char *)calloc(len+5, sizeof(char)); + + final_body[0] = '{'; + final_body[1] = '\n'; + + for (int i = 2; i < len+2; ++i){ + final_body[i] = tmp_body[i-2]; + } + + final_body[len+2] = '}'; + final_body[len+3] = '\n'; + + free(tmp_body); + free(tmp_prologue); + + return final_body; + +} + +void qb_free_stanza(quibble_stanza qs){ + free(qs.prologue); + free(qs.epilogue); + free(qs.name); + qb_free_kwarg_array(qs.kwargs, qs.num_kwargs); + qb_free_arg_array(qs.args, qs.num_args); +} + +/*----------------------------------------------------------------------------// + PRINTING +//----------------------------------------------------------------------------*/ + +void qb_print_stanza(quibble_stanza qs){ + printf("Quibble Stanza:\nName: %s\nPrologue:\n%s\nEpilogue\n%s\n", + qs.name, qs.prologue, qs.epilogue); + printf("Args...\n"); + for (int i = 0; i < qs.num_args; ++i){ + qb_print_arg(qs.args[i]); + } + printf("Kwargs...\n"); + for (int i = 0; i < qs.num_kwargs; ++i){ + qb_print_kwarg(qs.kwargs[i]); + } + printf("\n"); +} diff --git a/src/quibble_verses.c b/src/quibble_verses.c new file mode 100644 index 0000000..ab7e782 --- /dev/null +++ b/src/quibble_verses.c @@ -0,0 +1,152 @@ +/*-------------verses---------------------------------------------------------// + + Purpose: A quibble verse is a user-submitted function fragement to be compiled + later + +//----------------------------------------------------------------------------*/ + +#include "../include/quibble_program.h" + +bool qb_is_verse(char *verse, int offset){ + char substr[8] = "__verse"; + for (int i = 0; i < 7; ++i){ + if (verse[i+offset] != substr[i]){ + return false; + } + } + + return true; +} + +bool qb_find_keyword_in_verses(quibble_program qp, char *keyword){ + int found_index = -1; + for (int i = 0; i < qp.num_verses; ++i){ + found_index = qb_find_next_string(qp.verse_list[i].body, 0, keyword); + } + if (found_index >= 0){ + return true; + } + return false; +} + +quibble_verse qb_parse_verse(char *verse){ + int verse_size = strlen(verse); + int offset = 0; + bool qbinlined = qb_is_inlined(verse); + if (qbinlined){ + offset += 22; + } + + if (qb_is_verse(verse, offset)){ + offset += 7; + } + else { + fprintf(stderr, "Quibble verses must be configured with `__verse`!\n"); + exit(1); + } + + quibble_verse final_verse; + + int config_start = qb_find_next_char(verse, offset, '(')+1; + int config_end = qb_find_matching_char(verse, verse_size, config_start-1, '(', ')'); + + + if (config_end-config_start > 0){ + char *config = + (char *)calloc((config_end-config_start)+1, sizeof(char)); + + for (int i = 0; i < (config_end-config_start); ++i){ + config[i] = verse[config_start+i]; + } + + final_verse.num_args = qb_find_number_of_args(config); + final_verse.args = + qb_parse_args(config, final_verse.num_args); + + final_verse.num_kwargs = qb_find_number_of_kwargs(config); + final_verse.kwargs = + qb_parse_kwargs(config, final_verse.num_kwargs); + free(config); + } + else { + final_verse.args = NULL; + final_verse.num_args = 0; + final_verse.kwargs = NULL; + final_verse.num_kwargs = 0; + } + + int body_start = qb_find_next_char(verse, offset, '{')+1; + int body_end = + qb_find_matching_char(verse, verse_size, body_start-1, '{', '}'); + + if (body_end-body_start > 0){ + char *body = (char *)calloc((body_end-body_start)+1, sizeof(char)); + for (int i = 0; i < (body_end-body_start); ++i){ + body[i] = verse[body_start+i]; + } + + if (qbinlined){ + qb_preprocess_content(body); + } + final_verse.body = body; + } + else{ + final_verse.body = NULL; + } + final_verse.name = qb_strip_spaces(verse, offset, config_start-2); + + return final_verse; +} + +char *qb_expand_verse(quibble_program qp, char* verse_name, char *config){ + + int idx = qb_find_verse_index(qp, verse_name); + if (qp.verse_list[idx].body == NULL){ + return NULL; + } + char *tmp_prologue = qb_create_prologue(config, verse_name, + qp.verse_list[idx].args, qp.verse_list[idx].num_args, + qp.verse_list[idx].kwargs, qp.verse_list[idx].num_kwargs); + + char *tmp_body = (char *)calloc(MAX_META_SIZE, sizeof(char)); + strcat(tmp_body, tmp_prologue); + strcat(tmp_body, qp.verse_list[idx].body); + + int len = strlen(tmp_body); + char *final_body = (char *)calloc(len+5, sizeof(char)); + + final_body[0] = '{'; + final_body[1] = '\n'; + + for (int i = 2; i < len+2; ++i){ + final_body[i] = tmp_body[i-2]; + } + + final_body[len+2] = '}'; + final_body[len+3] = '\n'; + + free(tmp_body); + free(tmp_prologue); + return final_body; + +} + +void qb_free_verse(quibble_verse qv){ + free(qv.body); + free(qv.name); + qb_free_kwarg_array(qv.kwargs, qv.num_kwargs); + qb_free_arg_array(qv.args, qv.num_args); +} + +void qb_print_verse(quibble_verse qv){ + printf("Quibble Verse:\nName: %s\nBody:\n%s\n", qv.name, qv.body); + printf("Args...\n"); + for (int i = 0; i < qv.num_args; ++i){ + qb_print_arg(qv.args[i]); + } + printf("Kwargs...\n"); + for (int i = 0; i < qv.num_kwargs; ++i){ + qb_print_kwarg(qv.kwargs[i]); + } + printf("\n"); +} diff --git a/test/io_tests.c b/test/io_tests.c new file mode 100644 index 0000000..7014bfc --- /dev/null +++ b/test/io_tests.c @@ -0,0 +1,164 @@ +#include "tests.h" + +void quibble_io_tests(void){ + + printf("Quibble IO String tests...\n"); + + // QBINLINE + char *kernel_string = QBINLINE( + __verse check(){ + } + ); + + // qb_is_qbinlined + if (qb_is_inlined(kernel_string)){ + printf("\t"QBT_GREEN "Passed: " QBT_RESET "qb_is_qbinlined\n"); + } + else{ + printf("\t"QBT_RED "Failed: " QBT_RESET "qb_is_qbinlined\n"); + } + + // qb_preprocess_content + // QBINLINE creates a const, so copying to another container + char *check_string = qb_copy(kernel_string); + qb_preprocess_content(check_string); + if (strcmp(check_string, + "//\nQBINLINE\nGENERATED\n__verse\ncheck(){\n}") == 0){ + printf("\t"QBT_GREEN "Passed: " QBT_RESET "qb_preprocess_content\n"); + } + else{ + printf("\t"QBT_RED "Failed: " QBT_RESET "qb_preprocess_content\n"); + } + free(check_string); + + // Others + // qb_replace_char_if_proceeding + char check[23] = "This is a test string "; + qb_replace_char_if_proceeding(check, "test", ' ', '\n'); + qb_replace_char_if_proceeding(check, "T", ' ', '_'); + if (strcmp(check, "This_is a test\nstring ") == 0){ + printf("\t"QBT_GREEN "Passed: "QBT_RESET"qb_replace_char_if_proceeding\n"); + } + else{ + printf("\t"QBT_RED "Failed: "QBT_RESET"qb_replace_char_if_proceeding\n"); + } + + // qb_replace_char + qb_replace_char(check, 23, '\n', ' '); + if (strcmp(check, "This_is a test string ") == 0){ + printf("\t"QBT_GREEN "Passed: "QBT_RESET"qb_replace_char\n"); + } + else{ + printf("\t"QBT_RED "Failed: "QBT_RESET"qb_replace_char\n"); + } + + // qb_replace_next_char + qb_replace_next_char(check, 11, ' ', '_'); + if (strcmp(check, "This_is a test_string ") == 0){ + printf("\t"QBT_GREEN "Passed: "QBT_RESET"qb_replace_next_char\n"); + } + else{ + printf("\t"QBT_RED "Failed: "QBT_RESET"qb_replace_next_char\n"); + } + + // qb_find_occurrences + int occurrences = qb_find_occurrences(" ", check); + + if (occurrences == 3){ + printf("\t"QBT_GREEN "Passed: "QBT_RESET"qb_find_occurrences\n"); + } + else{ + printf("\t"QBT_RED "Failed: "QBT_RESET"qb_find_occurrences\n"); + } + + char *stripped_1 = qb_strip_spaces(check, 9, 14); + char *stripped_2 = qb_strip_spaces(check, 7, 9); + // qb_strip_spaces + if (strcmp(stripped_1, "test_") == 0 && + strcmp(stripped_2, "a") == 0){ + printf("\t"QBT_GREEN "Passed: "QBT_RESET"qb_strip_spaces\n"); + } + else{ + printf("\t"QBT_RED "Failed: "QBT_RESET"qb_strip_spaces\n"); + } + + free(stripped_1); + free(stripped_2); + + // qb_find_next_char + if (qb_find_next_char(check, 0, 't') == 10 && + qb_find_next_char(check, 0, 'z') == -1 && + qb_find_next_char(check, 11, 't') == 13){ + printf("\t"QBT_GREEN "Passed: "QBT_RESET"qb_find_next_char\n"); + } + else{ + printf("\t"QBT_RED "Failed: "QBT_RESET"qb_find_next_char\n"); + } + + // qb_find_next_string + if (qb_find_next_string(check, 0, "t") == 10 && + qb_find_next_string(check, 0, "test") == 10 && + qb_find_next_string(check, 0, "z") == -1 && + qb_find_next_string(check, 11, "test") == -1 && + qb_find_next_string(check, 11, "string") == 15){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_find_next_string\n"); + } + else{ + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_find_next_string\n"); + } + + // qb_find_matching_char + // "0123456789"; + char check_2[11] = "([]()(()))"; + // qb_find_next_string + if (qb_find_matching_char(check_2, 10, 0, '(', ')') == 9 && + qb_find_matching_char(check_2, 10, 1, '[', ']') == 2 && + qb_find_matching_char(check_2, 10, 5, '(', ')') == 8 && + qb_find_matching_char(check_2, 10, 7, '(', ')') == -1 && + qb_find_matching_char("(()((", 5, 0, '(', ')') == -1){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_find_matching_char\n"); + } + else{ + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_find_matching_char\n"); + } + + if (qb_find_type_size("bool") == sizeof(char) && + qb_find_type_size("char") == sizeof(char) && + qb_find_type_size("signed char") == sizeof(signed char) && + qb_find_type_size("unsigned char") == sizeof(unsigned char) && + qb_find_type_size("short") == sizeof(short) && + qb_find_type_size("short int") == sizeof(short int) && + qb_find_type_size("signed short") == sizeof(signed short) && + qb_find_type_size("signed short int") == sizeof(signed short int) && + qb_find_type_size("unsigned short") == sizeof(unsigned short) && + qb_find_type_size("unsigned short int") == sizeof(unsigned short int) && + qb_find_type_size("int") == sizeof(int) && + qb_find_type_size("signed") == sizeof(signed) && + qb_find_type_size("signed int") == sizeof(signed int) && + qb_find_type_size("unsigned") == sizeof(unsigned) && + qb_find_type_size("unsigned int") == sizeof(unsigned int) && + qb_find_type_size("long") == sizeof(long) && + qb_find_type_size("long int") == sizeof(long int) && + qb_find_type_size("signed long") == sizeof(signed long) && + qb_find_type_size("signed long int") == sizeof(signed long int) && + qb_find_type_size("unsigned long") == sizeof(unsigned long) && + qb_find_type_size("unsigned long int") == sizeof(unsigned long int) && + qb_find_type_size("long long") == sizeof(long long) && + qb_find_type_size("long long int") == sizeof(long long int) && + qb_find_type_size("signed long long") == sizeof(signed long long) && + qb_find_type_size("signed long long int") == sizeof(signed long long int) && + qb_find_type_size("unsigned long long") == sizeof(unsigned long long) && + qb_find_type_size("unsigned long long int") == sizeof(unsigned long long int) && + qb_find_type_size("float") == sizeof(float) && + qb_find_type_size("double") == sizeof(double) && + qb_find_type_size("long double") == sizeof(long double) && + qb_find_type_size("meh") == sizeof(int) && + qb_find_type_size("char *") == sizeof(char *)){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_find_type_size\n"); + } + else{ + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_find_type_size\n"); + } + + +} diff --git a/test/program_tests.c b/test/program_tests.c new file mode 100644 index 0000000..efbd990 --- /dev/null +++ b/test/program_tests.c @@ -0,0 +1,325 @@ +#include "tests.h" + +void quibble_arg_parsing_tests(void){ + + printf("Arg / Kwarg Parsing Tests...\n"); + // qb_parse_kwargs + char prologue_1[20] = " | float *x = 5;"; + char prologue_2[100] = "float *a , int b, double c | x = 5; y = 2*x+3;"; + char prologue_3[100] = "\t\n a, \t b, \n int c \n | \t\n int variable\t\n = \t\t\n whatever\n\n\t ;"; + char prologue_4[10] = "a,b,c"; + char prologue_5[10] = "meh| "; + char prologue_6[10] = "a=6;"; + char prologue_7[10] = " "; + int num_kwargs_1 = qb_find_number_of_kwargs(prologue_1); + int num_kwargs_2 = qb_find_number_of_kwargs(prologue_2); + int num_kwargs_3 = qb_find_number_of_kwargs(prologue_3); + int num_kwargs_4 = qb_find_number_of_kwargs(prologue_4); + int num_kwargs_5 = qb_find_number_of_kwargs(prologue_5); + int num_kwargs_6 = qb_find_number_of_kwargs(prologue_6); + int num_kwargs_7 = qb_find_number_of_kwargs(prologue_7); + + if (num_kwargs_1 == 1 && + num_kwargs_2 == 2 && + num_kwargs_3 == 1 && + num_kwargs_4 == 0 && + num_kwargs_5 == 0 && + num_kwargs_6 == 1 && + num_kwargs_7 == 0 ){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_find_number_of_kwargs\n"); + } + else { + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_find_number_of_kwargs\n"); + } + + quibble_kwarg *check_kwargs_1 = + qb_parse_kwargs(prologue_1, num_kwargs_1); + + quibble_kwarg *check_kwargs_2 = + qb_parse_kwargs(prologue_2, num_kwargs_2); + + quibble_kwarg *check_kwargs_3 = + qb_parse_kwargs(prologue_3, num_kwargs_3); + + quibble_kwarg *check_kwargs_4 = + qb_parse_kwargs(prologue_4, num_kwargs_4); + + quibble_kwarg *check_kwargs_5 = + qb_parse_kwargs(prologue_5, num_kwargs_5); + + quibble_kwarg *check_kwargs_6 = + qb_parse_kwargs(prologue_6, num_kwargs_6); + + quibble_kwarg *check_kwargs_7 = + qb_parse_kwargs(prologue_7, num_kwargs_7); + + if (strcmp(check_kwargs_1[0].variable, "x") == 0 && + strcmp(check_kwargs_1[0].type, "float *") == 0 && + strcmp(check_kwargs_1[0].value, "5") == 0 && + strcmp(check_kwargs_2[0].variable, "x") == 0 && + strcmp(check_kwargs_2[0].value, "5") == 0 && + strcmp(check_kwargs_2[1].variable, "y") == 0 && + strcmp(check_kwargs_2[1].value, "2*x+3") == 0 && + strcmp(check_kwargs_3[0].variable, "variable") == 0 && + strcmp(check_kwargs_3[0].value, "whatever") == 0 && + strcmp(check_kwargs_3[0].type, "int") == 0 && + check_kwargs_4 == NULL && check_kwargs_5 == NULL && + check_kwargs_7 == NULL && + strcmp(check_kwargs_6[0].variable, "a") == 0 && + strcmp(check_kwargs_6[0].value, "6") == 0 && + check_kwargs_6[0].type == NULL ){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_parse_kwargs\n"); + } + else { + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_parse_kwargs\n"); + } + + if (qb_find_kwarg_index(check_kwargs_2, num_kwargs_2, "x") == 0 && + qb_find_kwarg_index(check_kwargs_7, num_kwargs_7, "x") == -1 && + qb_find_kwarg_index(check_kwargs_3, num_kwargs_3, "variable") == 0 ){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_find_kwarg_index\n"); + } + else { + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_find_kwarg_index\n"); + } + + qb_free_kwarg_array(check_kwargs_1, num_kwargs_1); + qb_free_kwarg_array(check_kwargs_2, num_kwargs_2); + qb_free_kwarg_array(check_kwargs_3, num_kwargs_3); + qb_free_kwarg_array(check_kwargs_4, num_kwargs_4); + qb_free_kwarg_array(check_kwargs_5, num_kwargs_5); + qb_free_kwarg_array(check_kwargs_6, num_kwargs_6); + qb_free_kwarg_array(check_kwargs_7, num_kwargs_7); + + int num_args_1 = qb_find_number_of_args(prologue_1); + int num_args_2 = qb_find_number_of_args(prologue_2); + int num_args_3 = qb_find_number_of_args(prologue_3); + int num_args_4 = qb_find_number_of_args(prologue_4); + int num_args_5 = qb_find_number_of_args(prologue_5); + int num_args_6 = qb_find_number_of_args(prologue_6); + int num_args_7 = qb_find_number_of_args(prologue_6); + + if (num_args_1 == 0 && + num_args_2 == 3 && + num_args_3 == 3 && + num_args_4 == 3 && + num_args_5 == 1 && + num_args_6 == 0 && + num_args_7 == 0 ){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_find_number_of_args\n"); + } + else { + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_find_number_of_args\n"); + } + + quibble_arg *check_args_1 = qb_parse_args(prologue_1, num_args_1); + quibble_arg *check_args_2 = qb_parse_args(prologue_2, num_args_2); + quibble_arg *check_args_3 = qb_parse_args(prologue_3, num_args_3); + quibble_arg *check_args_4 = qb_parse_args(prologue_4, num_args_4); + quibble_arg *check_args_5 = qb_parse_args(prologue_5, num_args_5); + quibble_arg *check_args_6 = qb_parse_args(prologue_6, num_args_6); + quibble_arg *check_args_7 = qb_parse_args(prologue_7, num_args_7); + + if (check_args_1 == NULL && + strcmp(check_args_2[0].variable, "a") == 0 && + strcmp(check_args_2[0].type, "float *") == 0 && + strcmp(check_args_2[1].variable, "b") == 0 && + strcmp(check_args_2[1].type, "int") == 0 && + strcmp(check_args_2[2].variable, "c") == 0 && + strcmp(check_args_3[0].variable, "a") == 0 && + strcmp(check_args_3[1].variable, "b") == 0 && + strcmp(check_args_3[2].variable, "c") == 0 && + strcmp(check_args_3[2].type, "int") == 0 && + strcmp(check_args_4[0].variable, "a") == 0 && + strcmp(check_args_4[1].variable, "b") == 0 && + strcmp(check_args_4[2].variable, "c") == 0 && + strcmp(check_args_5[0].variable, "meh") == 0 && + check_args_6 == NULL && check_args_7 == NULL){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_parse_args\n"); + } + else { + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_parse_args\n"); + } + + if (qb_find_arg_index(check_args_5, num_args_5, "meh") == 0 && + qb_find_arg_index(check_args_7, num_args_7, "x") == -1 && + qb_find_arg_index(check_args_3, num_args_3, "b") == 1 ){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_find_arg_index\n"); + } + else { + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_find_arg_index\n"); + } + + qb_free_arg_array(check_args_1, num_args_1); + qb_free_arg_array(check_args_2, num_args_2); + qb_free_arg_array(check_args_3, num_args_3); + qb_free_arg_array(check_args_4, num_args_4); + qb_free_arg_array(check_args_5, num_args_5); + qb_free_arg_array(check_args_6, num_args_6); + qb_free_arg_array(check_args_7, num_args_7); + +} + +void quibble_program_tests(){ + printf("Program Parsing Tests...\n"); + + char *simple_program = QBINLINE( + __verse check(){} + __stanza check(){} + __poem check(){} + ); + + quibble_program qp_check = qb_parse_program(simple_program); + + if (qp_check.num_verses == 1 && + qp_check.everything_else == NULL && + strcmp(qp_check.verse_list[0].name, "check") == 0 && + qp_check.num_poems == 1 && + strcmp(qp_check.poem_list[0].name, "check") == 0 && + qp_check.num_stanzas == 1 && + strcmp(qp_check.stanza_list[0].name, "check") == 0 ){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_parse_program\n"); + } + else { + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_parse_program\n"); + } + + qb_free_program(qp_check); + + char *filename = qb_config_file("scribbles/example.qbl"); + quibble_program qp_2 = qb_parse_program_file(filename); + free(filename); + + if (qp_2.num_verses == 4 && + strcmp(qp_2.verse_list[0].name, "nothing") == 0 && + qp_2.num_poems == 4 && + strcmp(qp_2.poem_list[3].name, "poem_check") == 0 && + qp_2.num_stanzas == 4 && + strcmp(qp_2.stanza_list[0].name, "nothing") == 0 ){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_parse_program_file\n"); + } + else { + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_parse_program_file\n"); + } + + qb_configure_program(&qp_2, 0, 0); + int stanza_num = 0; + + int array_size = 10; + + // Getting the arrays ready + cl_int err; + + float *a = (float*)malloc(sizeof(float)*array_size); + float *b = (float*)malloc(sizeof(float)*array_size); + float *c = (float*)malloc(sizeof(float)*array_size); + + for (int i = 0; i < array_size; ++i){ + a[i] = 1; + b[i] = 1; + c[i] = 0; + } + + cl_mem d_a = clCreateBuffer(qp_2.context, + CL_MEM_READ_WRITE, + array_size * sizeof(float), + NULL, + &err); + + cl_check(err); + + cl_mem d_b = clCreateBuffer(qp_2.context, + CL_MEM_READ_WRITE, + array_size * sizeof(float), + NULL, + &err); + + cl_check(err); + + cl_mem d_c = clCreateBuffer(qp_2.context, + CL_MEM_READ_WRITE, + array_size * sizeof(float), + NULL, + &err); + + cl_check(err); + + cl_check( + clEnqueueWriteBuffer(qp_2.command_queue, + d_a, + CL_TRUE, + 0, + array_size * sizeof(float), + a, + 0, + NULL, + NULL) + ); + + cl_check( + clEnqueueWriteBuffer(qp_2.command_queue, + d_b, + CL_TRUE, + 0, + array_size * sizeof(float), + b, + 0, + NULL, + NULL) + ); + + cl_check( + clEnqueueWriteBuffer(qp_2.command_queue, + d_c, + CL_TRUE, + 0, + array_size * sizeof(float), + c, + 0, + NULL, + NULL) + ); + + qb_set_args(&qp_2, "poem_check", 3, "cl_mem a", &d_a, + "cl_mem b", &d_b, + "cl_mem c", &d_c); + qb_set_arg(&qp_2, "poem_check", "int stanza_num", &stanza_num); + qb_set_arg(&qp_2, "poem_check", "int array_size", &array_size); + + qb_run(qp_2, "poem_check", array_size, array_size); + + cl_check( + clEnqueueReadBuffer(qp_2.command_queue, + d_c, + CL_TRUE, + 0, + array_size * sizeof(float), + c, + 0, + NULL, + NULL) + ); + + bool result = true; + for (int i = 0; i < array_size; ++i){ + if (c[i] != 2.5){ + result = false; + } + } + + if (result){ + printf("\t"QBT_GREEN"Passed: "QBT_RESET"qb_configure_program\n"); + } + else { + printf("\t"QBT_RED"Failed: "QBT_RESET"qb_configure_program\n"); + } + + + free(a); + free(b); + free(c); + cl_check(clReleaseMemObject(d_a)); + cl_check(clReleaseMemObject(d_b)); + cl_check(clReleaseMemObject(d_c)); + qb_free_program(qp_2); +} diff --git a/test/tests.h b/test/tests.h new file mode 100644 index 0000000..08a07d5 --- /dev/null +++ b/test/tests.h @@ -0,0 +1,13 @@ +#ifndef QUIBBLE_TESTS +#define QUIBBLE_TESTS + +#include "../include/quibble_program.h" + +void quibble_io_tests(void); +void quibble_verse_tests(void); +void quibble_stanza_tests(void); +void quibble_poem_tests(void); +void quibble_program_tests(void); +void quibble_arg_parsing_tests(void); + +#endif