]> git.mdlowis.com Git - proto/sclpl-rb.git/commitdiff
partial implementation of canonical type lookup
authorMichael D. Lowis <mike.lowis@gentex.com>
Fri, 14 Feb 2020 21:30:39 +0000 (16:30 -0500)
committerMichael D. Lowis <mike.lowis@gentex.com>
Fri, 14 Feb 2020 21:30:39 +0000 (16:30 -0500)
atf [new file with mode: 0755]
atf.h [new file with mode: 0644]
compile.rb
foo.c [new file with mode: 0644]

diff --git a/atf b/atf
new file mode 100755 (executable)
index 0000000..a2bec4c
--- /dev/null
+++ b/atf
@@ -0,0 +1,349 @@
+#!/usr/bin/env bash
+
+runner="runner.c"
+declare -a sources
+header=$(cat<<-EOF
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#ifdef ATF_TEST
+    #define main MAIN
+    #define UNITTEST(name,...) void name(void)
+#else
+    #define UNITTEST(name,...) static inline void name(void)
+#endif
+
+#define CHECK(cond) Assert(cond, #cond, __FILE__, __LINE__)
+
+typedef struct Value {
+    struct Value* next;
+    void (*showfn)(struct Value* val);
+    long ndata;
+    long data[];
+} Value;
+
+void Assert(int cond, char* condstr, char* file, int line);
+void* ValueAlloc(void (*showfn)(Value* val), size_t num, size_t sz, void* data);
+long RandR(long from, long to);
+long Rand(void);
+
+void ShowLong(Value* val);
+void ShowBool(Value* val);
+void ShowChar(Value* val);
+void ShowString(Value* val);
+
+long MkLongR(long from, long to);
+long MkLong(void);
+uint8_t MkU8(void);
+uint16_t MkU16(void);
+uint32_t MkU32(void);
+bool MkBool(void);
+char MkAsciiChar(void);
+char* MkAsciiStringL(size_t len);
+char* MkAsciiString(void);
+EOF
+)
+
+# get the list of sources
+for i do
+    case "$i" in
+        *.c) sources+=("$i") ;;
+        --header)
+            printf "%s\n" "$header"
+            exit 0
+            ;;
+    esac
+done
+
+cat <<-EOF > "$runner"
+#include "atf.h"
+#undef main
+#include <setjmp.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <time.h>
+
+#define RUN(file, line, name, ntrials) \\
+    void name(void); \\
+    runtestn(file, line, #name, name, ntrials)
+
+static char* Curr_File = 0;
+static char* Curr_Test = 0;
+static char* Curr_Expr = 0;
+static unsigned int Curr_Line = 0;
+static Value* Curr_Values;
+static jmp_buf Jump_Buf;
+static unsigned long Total = 0;
+static unsigned long Failed = 0;
+static uintptr_t Heap_Buf[1024*1024];
+static uintptr_t* Heap_Top = Heap_Buf;
+static uintptr_t* Heap_Base = Heap_Buf;
+static uintptr_t* Heap_End = Heap_Buf + sizeof(Heap_Buf)/sizeof(uintptr_t) - 1;
+enum {
+    FAIL_ASSERT = 1,
+    FAIL_OOM = 2
+};
+
+/* Basic Runtime Functions
+ ****************************/
+static int print_results(void)
+{
+    printf("%lu/%lu tests passed\n" , Total - Failed, Total);
+    return Failed;
+}
+
+static void print_values(void)
+{
+    Value* values = 0;
+    while (Curr_Values)
+    {
+        Value* val = Curr_Values;
+        Curr_Values = val->next;
+        val->next = values;
+        values = val;
+    }
+
+    while (values)
+    {
+        printf("  -> arg: ");
+        values->showfn(values);
+        values = values->next;
+    }
+}
+
+void* malloc(size_t size)
+{
+    void* ptr = Heap_Top;
+    size_t num_words = ((size & ~0x7) + ((size & 7) ? 1 : 0)) / sizeof(uintptr_t) + 1;
+    *Heap_Top = size;
+    Heap_Top += num_words;
+    if (Heap_Top > Heap_End)
+    {
+        Heap_Top = Heap_Base; // Reset heap in case printf mallocs.
+        fprintf(stderr,"%s:%d: MEM %s out of memory\n", Curr_File, Curr_Line, Curr_Test);
+        longjmp(Jump_Buf, FAIL_OOM);
+    }
+    else
+    {
+        memset(ptr, 0, size);
+    }
+    return ptr;
+}
+
+void* calloc(size_t num, size_t size)
+{
+    return malloc(num * size);
+}
+
+void* realloc(void* ptr, size_t new_size)
+{
+    uintptr_t old_size = *((uintptr_t*)ptr - 1);
+    void* newptr = malloc(new_size);
+    memcpy(newptr, ptr, old_size);
+    return newptr;
+}
+
+void free(void* ptr)
+{
+    (void)ptr; /* simply reset the buffer after each test */
+}
+
+void Assert(int cond, char* condstr, char* file, int line)
+{
+    if (!cond)
+    {
+        Curr_Expr = condstr;
+        longjmp(Jump_Buf, 1);
+    }
+}
+
+static int runtest(void (*fn)(void))
+{
+    Curr_Values = 0;
+    Heap_Top = Heap_Base;
+    int code = setjmp(Jump_Buf);
+    if (code == 0)
+    {
+        fn();
+    }
+    return code;
+}
+
+static void runtestn(char* file, int line, char* fnname, void (*fn)(void), int ntrials)
+{
+    Curr_File = file;
+    Curr_Line = line;
+    Curr_Test = fnname;
+    Total++;
+    for (int i = 0; i < ntrials; i++)
+    {
+        int fail = runtest(fn);
+        if (fail != 0)
+        {
+            Failed++;
+            if (fail == FAIL_ASSERT)
+            {
+                if (ntrials == 1)
+                {
+                    printf("%s:%d: FAIL for test %s \n  -> CHECK( %s )\n", Curr_File, Curr_Line, Curr_Test, Curr_Expr);
+                }
+                else
+                {
+                    printf("%s:%d: FAIL on trial %d/%d for test %s\n", Curr_File, Curr_Line, i, ntrials, Curr_Test);
+                    print_values();
+                }
+            }
+            break;
+        }
+    }
+}
+
+static void handle_signal(int sig)
+{
+    /* Determine the signal name */
+    char* sig_name = NULL;
+    switch(sig)
+    {
+        case SIGABRT: sig_name = "SIGABRT"; break;
+        case SIGBUS:  sig_name = "SIGBUS";  break;
+        case SIGFPE:  sig_name = "SIGFPE";  break;
+        case SIGILL:  sig_name = "SIGILL";  break;
+        case SIGSEGV: sig_name = "SIGSEGV"; break;
+        case SIGSYS:  sig_name = "SIGSYS";  break;
+        /* If we don't recognize it then just return and let the default handler
+           catch it. */
+        default:      return;
+    }
+    /* Error and exit. No summary will be printed but the user will know which
+       test has crashed. */
+    fprintf(stderr,"%s:%d: CRASH %s (signal: %d - %s)\n", Curr_File, Curr_Line, Curr_Test, sig, sig_name);
+    Failed++;
+    (void)print_results();
+    _Exit(1);
+}
+
+/* Property Testing Functions
+ ****************************/
+void* ValueAlloc(void (*showfn)(Value* val), size_t num, size_t sz, void* data)
+{
+    Value* value = calloc(num, sizeof(Value) + sz);
+    value->showfn = showfn;
+    value->ndata = num;
+    value->next = Curr_Values;
+    Curr_Values = value;
+    if (data)
+    {
+        memcpy(value->data, data, num * sz);
+    }
+    return &(value->data[0]);
+}
+
+long RandR(long from, long to)
+{
+    return ((rand() % (to - from + 1)) + from);
+}
+
+long Rand(void)
+{
+    return rand();
+}
+
+void ShowLong(Value* val)
+{
+    printf("%ld\n", (val->data[0]));
+}
+
+void ShowBool(Value* val)
+{
+    printf("%s\n", (val->data[0] ? "true" : "false"));
+}
+
+void ShowChar(Value* val)
+{
+    printf("'%c'\n", (char)(val->data[0]));
+}
+
+void ShowString(Value* val)
+{
+    printf("'%s'\n", (char*)(val->data));
+}
+
+long MkLongR(long from, long to)
+{
+    return *((long*)ValueAlloc(ShowLong, 1, sizeof(long), &(long){RandR(from, to)}));
+}
+
+long MkLong(void)
+{
+    return *((long*)ValueAlloc(ShowLong, 1, sizeof(long), &(long){Rand()}));
+}
+
+uint8_t MkU8(void)
+{
+    return MkLongR(0, UINT8_MAX);
+}
+
+uint16_t MkU16(void)
+{
+    return MkLongR(0, UINT16_MAX);
+}
+
+uint32_t MkU32(void)
+{
+    return MkLongR(0, UINT32_MAX);
+}
+
+bool MkBool(void)
+{
+    return *((bool*)ValueAlloc(ShowBool, 1, sizeof(bool), &(bool){RandR(0, 1)}));
+}
+
+char MkAsciiChar(void)
+{
+    return *((char*)ValueAlloc(ShowChar, 1, sizeof(char), &(char){RandR(32, 127)}));
+}
+
+char* MkAsciiStringL(size_t len) {
+    char* val = ValueAlloc(ShowString, len+1, sizeof(char), 0);
+    for (size_t i = 0; i < len; i++)
+    {
+        *(val++) = RandR(32, 127);
+    }
+    return val;
+}
+
+char* MkAsciiString(void) {
+    return MkAsciiStringL(RandR(1, 1024));
+}
+
+/* Main Routine
+ ****************************/
+int main(int argc, char** argv)
+{
+    unsigned int seed = (argc >= 2 ? strtoul(argv[1], 0, 0) : time(0));
+    printf("Seed: %u\n", seed);
+    srand(seed);
+    signal(SIGABRT, handle_signal);
+    signal(SIGBUS,  handle_signal);
+    signal(SIGFPE,  handle_signal);
+    signal(SIGILL,  handle_signal);
+    signal(SIGSEGV, handle_signal);
+    signal(SIGSYS,  handle_signal);
+    Heap_Base = Heap_Top;
+EOF
+grep -Hn '\(PROP\|UNIT\)TEST' "${sources[@]}" | sed '
+    s/^\(.*\):\(.*\):[\t ]*UNITTEST(\(.*\),\(.*\)).*$/    RUN("\1",\2,\3,\4);/
+    s/^\(.*\):\(.*\):[\t ]*UNITTEST(\(.*\)).*$/    RUN("\1",\2,\3,1);/
+' >> "$runner"
+cat <<-EOF >> "$runner"
+    return print_results();
+}
+EOF
+
+cc -D ATF_TEST -g -o runner -O0 "$@" "$runner" && ./runner
+rm -f runner "$runner"
diff --git a/atf.h b/atf.h
new file mode 100644 (file)
index 0000000..5ce11c3
--- /dev/null
+++ b/atf.h
@@ -0,0 +1,39 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#ifdef ATF_TEST
+    #define main MAIN
+    #define UNITTEST(name,...) void name(void)
+#else
+    #define UNITTEST(name,...) static inline void name(void)
+#endif
+
+#define CHECK(cond) Assert(cond, #cond, __FILE__, __LINE__)
+
+typedef struct Value {
+    struct Value* next;
+    void (*showfn)(struct Value* val);
+    long ndata;
+    long data[];
+} Value;
+
+void Assert(int cond, char* condstr, char* file, int line);
+void* ValueAlloc(void (*showfn)(Value* val), size_t num, size_t sz, void* data);
+long RandR(long from, long to);
+long Rand(void);
+
+void ShowLong(Value* val);
+void ShowBool(Value* val);
+void ShowChar(Value* val);
+void ShowString(Value* val);
+
+long MkLongR(long from, long to);
+long MkLong(void);
+uint8_t MkU8(void);
+uint16_t MkU16(void);
+uint32_t MkU32(void);
+bool MkBool(void);
+char MkAsciiChar(void);
+char* MkAsciiStringL(size_t len);
+char* MkAsciiString(void);
index d6c257840309d80e324d1c9df754410ac8a57bfa..be996f354d57c5a33ede4a71eb214e3dda9e7551 100755 (executable)
@@ -84,14 +84,15 @@ class Symtable
   end
 
   def canonical_type(type)
-    return name if not type.is_a? String
-    while type.is_a? String
-      newtype = self[type]
-      raise "unknown type #{type}" if newtype.nil?
-      raise "#{type} is not a type (kind: #{type[:kind]})" if (type[:kind] != :type)
-      type = newtype
+#    return type if not type.is_a? String
+    newtype = type
+    while newtype.is_a? String
+      basetype = self[newtype]
+      raise "unknown type #{basetype}" if basetype.nil?
+      raise "#{basetype} is not a type (kind: #{basetype[:kind]})" if (basetype[:kind] != :type)
+      newtype = basetype[:type]
     end
-    type
+    newtype
   end
 end
 
@@ -631,7 +632,6 @@ pkg = Package.new("lib/main.m")
 syms = Symtable.new()
 pkg.definitions.each do |name, val|
   next if val[:kind] == :module
-#  pp val
   type = syms.canonical_type(val[:type])
   puts name + " ->"
   pp type
diff --git a/foo.c b/foo.c
new file mode 100644 (file)
index 0000000..36cc1f6
--- /dev/null
+++ b/foo.c
@@ -0,0 +1,17 @@
+#include "atf.h"
+#include <stdio.h>
+
+UNITTEST(Foo,10)
+{
+    CHECK(0);
+}
+
+UNITTEST(Bar)
+{
+    CHECK(1 == 0);
+}
+
+int main()
+{
+    return 0;
+}
\ No newline at end of file