summaryrefslogtreecommitdiffhomepage
path: root/samples/12_c_extensions/02_intermediate
diff options
context:
space:
mode:
authorAmir Rajan <[email protected]>2020-10-13 00:45:16 -0500
committerAmir Rajan <[email protected]>2020-10-13 00:48:54 -0500
commit05cbef7fb8224332795e5685be499d81d20e7d93 (patch)
tree13ec5f1755c2f45618741f2f016ed8729dbedd41 /samples/12_c_extensions/02_intermediate
parentabad948c1154d88d79b9f891e3b7315540e0b0a3 (diff)
downloaddragonruby-game-toolkit-contrib-05cbef7fb8224332795e5685be499d81d20e7d93.tar.gz
dragonruby-game-toolkit-contrib-05cbef7fb8224332795e5685be499d81d20e7d93.zip
Synced with 1.26.
Diffstat (limited to 'samples/12_c_extensions/02_intermediate')
-rw-r--r--samples/12_c_extensions/02_intermediate/README.md187
-rw-r--r--samples/12_c_extensions/02_intermediate/app/main.rb19
-rw-r--r--samples/12_c_extensions/02_intermediate/app/re.c527
-rw-r--r--samples/12_c_extensions/02_intermediate/app/re.h65
-rw-r--r--samples/12_c_extensions/02_intermediate/c-regex-demo.pngbin0 -> 135717 bytes
-rw-r--r--samples/12_c_extensions/02_intermediate/license-for-regex-library.txt24
-rw-r--r--samples/12_c_extensions/02_intermediate/license-for-sample.txt9
-rw-r--r--samples/12_c_extensions/02_intermediate/metadata/game_metadata.txt5
-rw-r--r--samples/12_c_extensions/02_intermediate/native/re-bindings.c282
-rw-r--r--samples/12_c_extensions/02_intermediate/pre.bat5
-rwxr-xr-xsamples/12_c_extensions/02_intermediate/pre.sh19
11 files changed, 1142 insertions, 0 deletions
diff --git a/samples/12_c_extensions/02_intermediate/README.md b/samples/12_c_extensions/02_intermediate/README.md
new file mode 100644
index 0000000..a597944
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/README.md
@@ -0,0 +1,187 @@
+# C Extensions introduction
+
+This sample app is the next step on mastering C extensions for the
+DragonRuby Game Toolkit. You'll need a Pro License which can be purchased at
+http://dragonruby.org. The sample app is provided in the Standard license for
+those that are curious as to what implementing C Extensions looks like.
+
+## Requirements
+
+Make sure you went through the first example '01_basics'.
+
+## Regular Expressions
+
+This sample shows how to use a third-party C library. We use a library for
+regular expressions: https://github.com/kokke/tiny-regex-c.
+
+The library comes with two files `re.c` and `re.h`. We need to generate bindings
+from the header file. By default, DragonRuby generates bindings under `CExt`
+module. This can be overriden by `--ffi-module=` command line flag.
+
+Run the following command from Linux/macOS terminal:
+
+```
+> ./dragonruby-bind --output=mygame/re-bindings.c --ffi-module=RE mygame/re.h
+```
+
+Or from Windows Powershell:
+
+```
+> .\dragobruby-bind.exe --output=mygame\re-bindings.c --ffi-module=RE mygame\re.h
+```
+
+Now you can compile the library together with the generated bindings.
+
+On Windows:
+
+```
+clang -shared \
+ --sysroot=C:\mingw-w64\mingw64^
+ --target=x86_64-w64-mingw32 -fuse-ld=lld^
+ -isystem include -I . -fPIC^
+ -o mygame\native\windows-amd64\ext.dll mygame\re.c mygame\re-bindings.c
+```
+
+On Linux:
+
+```
+> clang -shared \
+ -isystem include -I . -fPIC \
+ -o mygame/native/linux-amd64/ext.so mygame/re-bindings.c mygame/re.c
+```
+
+On Raspberry Pi, on the device:
+
+```
+> clang -shared \
+ -isystem include -I . -fPIC \
+ -o mygame/native/linux-raspberrypi/ext.so mygame/re-bindings.c mygame/re.c
+```
+
+On Raspberry Pi, with a cross-compiler:
+
+```
+> clang -shared --sysroot=/where/i/installed/a/sysroot \
+ --target=arm-linux-gnueabihf -fuse-ld=lld \
+ -isystem include -I . -fPIC \
+ -o mygame/native/linux-raspberrypi/ext.so mygame/re-bindings.c mygame/re.c
+```
+
+On macOS:
+
+```
+> clang -shared \
+ -isystem include -I . -fPIC \
+ -o mygame/native/macos/ext.dylib mygame/re-bindings.c mygame/re.c
+```
+
+On Android:
+
+(This is what it looks like on Linux, change the appropriate parts for Windows, etc)
+
+```
+> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \
+ --target=armv7-none-linux-androideabi26 \
+ --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \
+ --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
+ -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a \
+ -mthumb -fno-limit-debug-info -fPIC -shared \
+ -o mygame/native/android-arm32/ext.so mygame/re-bindings.c mygame/re.c
+
+> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \
+ --target=aarch64-none-linux-android26 \
+ --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \
+ --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
+ -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \
+ -fno-limit-debug-info -fPIC -shared \
+ -o mygame/native/android-arm64/ext.so mygame/re-bindings.c mygame/re.c
+
+> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \
+ --target=i686-none-linux-android26 \
+ --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \
+ --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
+ -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \
+ -fno-limit-debug-info -fPIC -shared \
+ -o mygame/native/android-x86/ext.so mygame/re-bindings.c mygame/re.c
+
+> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \
+ --target=x86_64-none-linux-android26 \
+ --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \
+ --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
+ -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \
+ -fno-limit-debug-info -fPIC -shared \
+ -o mygame/native/android-amd64/ext.so mygame/re-bindings.c mygame/re.c
+```
+
+Now, the C extension is available under the `FFI::RE` namespace.
+
+```
+$gtk.ffi_misc.gtk_dlopen("ext")
+include FFI::RE
+```
+
+The `tiny-regex-c` exposes three functions, we are interested in the last one:
+
+```c
+int re_match(const char* pattern, const char* text, int* matchlength);
+```
+
+The first argument is the regex pattern to search for. The second one is the
+actual text we want to search through. The last argument is a pointer to an
+integer: it stores the location of the last character found in the text.
+The function returns the first location of the found pattern, or -1 if the
+pattern is not found. The usage in DragonRuby is straightforward:
+
+```ruby
+$gtk.ffi_misc.gtk_dlopen("ext")
+include FFI::RE
+
+input = "<<Hello, DragonRiders!>>"
+last = IntPointer.new
+first = re_match("\\w+", input, last)
+if first != -1
+ puts "Found a word (#{first}, #{last.value}): '#{input.slice(first, last.value)}'"
+end
+
+def tick args
+end
+```
+
+When you run the game you should see `Found a word (2, 5): 'Hello'` on the console.
+
+Two things are worth mentioning. The C API requires `const char *` and `int *`.
+In case of strings, DragonRuby does the transformation for you transparently.
+In the case of `int *` you have to create the right pointer manually (`IntPointer.new`).
+The good thing is that you don't need to free or deallocate this memory manually:
+DragonRuby also does this for you transparently.
+
+With this knowledge, we can now create a slightly more complex function that
+splits input into an array of words:
+
+```ruby
+$gtk.ffi_misc.gtk_dlopen("ext")
+include FFI::RE
+
+def split_words(input)
+ words = []
+ last = IntPointer.new
+ re = re_compile("\\w+")
+ first = re_matchp(re, input, last)
+ while first != -1
+ words << input.slice(first, last.value)
+ input = input.slice(last.value + first, input.length)
+ first = re_matchp(re, input, last)
+ end
+ words
+end
+
+input = "<<Hello, DragonRiders!>>"
+
+def tick args
+ args.outputs.labels << [640, 500, split_words(input).join(' '), 5, 1]
+end
+```
+
+Now, you should see a big label with "Hello DragonRiders" on it.
+
+![C Regex](c-regex-demo.png)
diff --git a/samples/12_c_extensions/02_intermediate/app/main.rb b/samples/12_c_extensions/02_intermediate/app/main.rb
new file mode 100644
index 0000000..2e3fd7c
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/app/main.rb
@@ -0,0 +1,19 @@
+$gtk.ffi_misc.gtk_dlopen("ext")
+include FFI::RE
+
+def split_words(input)
+ words = []
+ last = IntPointer.new
+ re = re_compile("\\w+")
+ first = re_matchp(re, input, last)
+ while first != -1
+ words << input.slice(first, last.value)
+ input = input.slice(last.value + first, input.length)
+ first = re_matchp(re, input, last)
+ end
+ words
+end
+
+def tick args
+ args.outputs.labels << [640, 500, split_words("hello, dragonriders!").join(' '), 5, 1]
+end
diff --git a/samples/12_c_extensions/02_intermediate/app/re.c b/samples/12_c_extensions/02_intermediate/app/re.c
new file mode 100644
index 0000000..2d64d9f
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/app/re.c
@@ -0,0 +1,527 @@
+/*
+ *
+ * Mini regex-module inspired by Rob Pike's regex code described in:
+ *
+ * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
+ *
+ *
+ *
+ * Supports:
+ * ---------
+ * '.' Dot, matches any character
+ * '^' Start anchor, matches beginning of string
+ * '$' End anchor, matches end of string
+ * '*' Asterisk, match zero or more (greedy)
+ * '+' Plus, match one or more (greedy)
+ * '?' Question, match zero or one (non-greedy)
+ * '[abc]' Character class, match if one of {'a', 'b', 'c'}
+ * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken!
+ * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z }
+ * '\s' Whitespace, \t \f \r \n \v and spaces
+ * '\S' Non-whitespace
+ * '\w' Alphanumeric, [a-zA-Z0-9_]
+ * '\W' Non-alphanumeric
+ * '\d' Digits, [0-9]
+ * '\D' Non-digits
+ *
+ *
+ */
+
+
+
+#include "re.h"
+#include <stdio.h>
+
+/* Definitions: */
+
+#define MAX_REGEXP_OBJECTS 30 /* Max number of regex symbols in expression. */
+#define MAX_CHAR_CLASS_LEN 40 /* Max length of character-class buffer in. */
+
+
+enum { UNUSED, DOT, BEGIN, END, QUESTIONMARK, STAR, PLUS, CHAR, CHAR_CLASS, INV_CHAR_CLASS, DIGIT, NOT_DIGIT, ALPHA, NOT_ALPHA, WHITESPACE, NOT_WHITESPACE, /* BRANCH */ };
+
+typedef struct regex_t
+{
+ unsigned char type; /* CHAR, STAR, etc. */
+ union
+ {
+ unsigned char ch; /* the character itself */
+ unsigned char* ccl; /* OR a pointer to characters in class */
+ };
+} regex_t;
+
+
+
+/* Private function declarations: */
+static int matchpattern(regex_t* pattern, const char* text, int* matchlength);
+static int matchcharclass(char c, const char* str);
+static int matchstar(regex_t p, regex_t* pattern, const char* text, int* matchlength);
+static int matchplus(regex_t p, regex_t* pattern, const char* text, int* matchlength);
+static int matchone(regex_t p, char c);
+static int matchdigit(char c);
+static int matchalpha(char c);
+static int matchwhitespace(char c);
+static int matchmetachar(char c, const char* str);
+static int matchrange(char c, const char* str);
+static int matchdot(char c);
+static int ismetachar(char c);
+
+
+
+/* Public functions: */
+int re_match(const char* pattern, const char* text, int* matchlength)
+{
+ return re_matchp(re_compile(pattern), text, matchlength);
+}
+
+int re_matchp(re_t pattern, const char* text, int* matchlength)
+{
+ *matchlength = 0;
+ if (pattern != 0)
+ {
+ if (pattern[0].type == BEGIN)
+ {
+ return ((matchpattern(&pattern[1], text, matchlength)) ? 0 : -1);
+ }
+ else
+ {
+ int idx = -1;
+
+ do
+ {
+ idx += 1;
+
+ if (matchpattern(pattern, text, matchlength))
+ {
+ if (text[0] == '\0')
+ return -1;
+
+ return idx;
+ }
+ }
+ while (*text++ != '\0');
+ }
+ }
+ return -1;
+}
+
+re_t re_compile(const char* pattern)
+{
+ /* The sizes of the two static arrays below substantiates the static RAM usage of this module.
+ MAX_REGEXP_OBJECTS is the max number of symbols in the expression.
+ MAX_CHAR_CLASS_LEN determines the size of buffer for chars in all char-classes in the expression. */
+ static regex_t re_compiled[MAX_REGEXP_OBJECTS];
+ static unsigned char ccl_buf[MAX_CHAR_CLASS_LEN];
+ int ccl_bufidx = 1;
+
+ char c; /* current char in pattern */
+ int i = 0; /* index into pattern */
+ int j = 0; /* index into re_compiled */
+
+ while (pattern[i] != '\0' && (j+1 < MAX_REGEXP_OBJECTS))
+ {
+ c = pattern[i];
+
+ switch (c)
+ {
+ /* Meta-characters: */
+ case '^': { re_compiled[j].type = BEGIN; } break;
+ case '$': { re_compiled[j].type = END; } break;
+ case '.': { re_compiled[j].type = DOT; } break;
+ case '*': { re_compiled[j].type = STAR; } break;
+ case '+': { re_compiled[j].type = PLUS; } break;
+ case '?': { re_compiled[j].type = QUESTIONMARK; } break;
+/* case '|': { re_compiled[j].type = BRANCH; } break; <-- not working properly */
+
+ /* Escaped character-classes (\s \w ...): */
+ case '\\':
+ {
+ if (pattern[i+1] != '\0')
+ {
+ /* Skip the escape-char '\\' */
+ i += 1;
+ /* ... and check the next */
+ switch (pattern[i])
+ {
+ /* Meta-character: */
+ case 'd': { re_compiled[j].type = DIGIT; } break;
+ case 'D': { re_compiled[j].type = NOT_DIGIT; } break;
+ case 'w': { re_compiled[j].type = ALPHA; } break;
+ case 'W': { re_compiled[j].type = NOT_ALPHA; } break;
+ case 's': { re_compiled[j].type = WHITESPACE; } break;
+ case 'S': { re_compiled[j].type = NOT_WHITESPACE; } break;
+
+ /* Escaped character, e.g. '.' or '$' */
+ default:
+ {
+ re_compiled[j].type = CHAR;
+ re_compiled[j].ch = pattern[i];
+ } break;
+ }
+ }
+ /* '\\' as last char in pattern -> invalid regular expression. */
+/*
+ else
+ {
+ re_compiled[j].type = CHAR;
+ re_compiled[j].ch = pattern[i];
+ }
+*/
+ } break;
+
+ /* Character class: */
+ case '[':
+ {
+ /* Remember where the char-buffer starts. */
+ int buf_begin = ccl_bufidx;
+
+ /* Look-ahead to determine if negated */
+ if (pattern[i+1] == '^')
+ {
+ re_compiled[j].type = INV_CHAR_CLASS;
+ i += 1; /* Increment i to avoid including '^' in the char-buffer */
+ if (pattern[i+1] == 0) /* incomplete pattern, missing non-zero char after '^' */
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ re_compiled[j].type = CHAR_CLASS;
+ }
+
+ /* Copy characters inside [..] to buffer */
+ while ( (pattern[++i] != ']')
+ && (pattern[i] != '\0')) /* Missing ] */
+ {
+ if (pattern[i] == '\\')
+ {
+ if (ccl_bufidx >= MAX_CHAR_CLASS_LEN - 1)
+ {
+ //fputs("exceeded internal buffer!\n", stderr);
+ return 0;
+ }
+ if (pattern[i+1] == 0) /* incomplete pattern, missing non-zero char after '\\' */
+ {
+ return 0;
+ }
+ ccl_buf[ccl_bufidx++] = pattern[i++];
+ }
+ else if (ccl_bufidx >= MAX_CHAR_CLASS_LEN)
+ {
+ //fputs("exceeded internal buffer!\n", stderr);
+ return 0;
+ }
+ ccl_buf[ccl_bufidx++] = pattern[i];
+ }
+ if (ccl_bufidx >= MAX_CHAR_CLASS_LEN)
+ {
+ /* Catches cases such as [00000000000000000000000000000000000000][ */
+ //fputs("exceeded internal buffer!\n", stderr);
+ return 0;
+ }
+ /* Null-terminate string end */
+ ccl_buf[ccl_bufidx++] = 0;
+ re_compiled[j].ccl = &ccl_buf[buf_begin];
+ } break;
+
+ /* Other characters: */
+ default:
+ {
+ re_compiled[j].type = CHAR;
+ re_compiled[j].ch = c;
+ } break;
+ }
+ /* no buffer-out-of-bounds access on invalid patterns - see https://github.com/kokke/tiny-regex-c/commit/1a279e04014b70b0695fba559a7c05d55e6ee90b */
+ if (pattern[i] == 0)
+ {
+ return 0;
+ }
+
+ i += 1;
+ j += 1;
+ }
+ /* 'UNUSED' is a sentinel used to indicate end-of-pattern */
+ re_compiled[j].type = UNUSED;
+
+ return (re_t) re_compiled;
+}
+
+void re_print(regex_t* pattern)
+{
+ const char* types[] = { "UNUSED", "DOT", "BEGIN", "END", "QUESTIONMARK", "STAR", "PLUS", "CHAR", "CHAR_CLASS", "INV_CHAR_CLASS", "DIGIT", "NOT_DIGIT", "ALPHA", "NOT_ALPHA", "WHITESPACE", "NOT_WHITESPACE", "BRANCH" };
+
+ int i;
+ int j;
+ char c;
+ for (i = 0; i < MAX_REGEXP_OBJECTS; ++i)
+ {
+ if (pattern[i].type == UNUSED)
+ {
+ break;
+ }
+
+ printf("type: %s", types[pattern[i].type]);
+ if (pattern[i].type == CHAR_CLASS || pattern[i].type == INV_CHAR_CLASS)
+ {
+ printf(" [");
+ for (j = 0; j < MAX_CHAR_CLASS_LEN; ++j)
+ {
+ c = pattern[i].ccl[j];
+ if ((c == '\0') || (c == ']'))
+ {
+ break;
+ }
+ printf("%c", c);
+ }
+ printf("]");
+ }
+ else if (pattern[i].type == CHAR)
+ {
+ printf(" '%c'", pattern[i].ch);
+ }
+ printf("\n");
+ }
+}
+
+
+
+/* Private functions: */
+static int matchdigit(char c)
+{
+ return ((c >= '0') && (c <= '9'));
+}
+static int matchalpha(char c)
+{
+ return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
+}
+static int matchwhitespace(char c)
+{
+ return ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\f') || (c == '\v'));
+}
+static int matchalphanum(char c)
+{
+ return ((c == '_') || matchalpha(c) || matchdigit(c));
+}
+static int matchrange(char c, const char* str)
+{
+ return ( (c != '-')
+ && (str[0] != '\0')
+ && (str[0] != '-')
+ && (str[1] == '-')
+ && (str[2] != '\0')
+ && ( (c >= str[0])
+ && (c <= str[2])));
+}
+static int matchdot(char c)
+{
+#if defined(RE_DOT_MATCHES_NEWLINE) && (RE_DOT_MATCHES_NEWLINE == 1)
+ (void)c;
+ return 1;
+#else
+ return c != '\n' && c != '\r';
+#endif
+}
+static int ismetachar(char c)
+{
+ return ((c == 's') || (c == 'S') || (c == 'w') || (c == 'W') || (c == 'd') || (c == 'D'));
+}
+
+static int matchmetachar(char c, const char* str)
+{
+ switch (str[0])
+ {
+ case 'd': return matchdigit(c);
+ case 'D': return !matchdigit(c);
+ case 'w': return matchalphanum(c);
+ case 'W': return !matchalphanum(c);
+ case 's': return matchwhitespace(c);
+ case 'S': return !matchwhitespace(c);
+ default: return (c == str[0]);
+ }
+}
+
+static int matchcharclass(char c, const char* str)
+{
+ do
+ {
+ if (matchrange(c, str))
+ {
+ return 1;
+ }
+ else if (str[0] == '\\')
+ {
+ /* Escape-char: increment str-ptr and match on next char */
+ str += 1;
+ if (matchmetachar(c, str))
+ {
+ return 1;
+ }
+ else if ((c == str[0]) && !ismetachar(c))
+ {
+ return 1;
+ }
+ }
+ else if (c == str[0])
+ {
+ if (c == '-')
+ {
+ return ((str[-1] == '\0') || (str[1] == '\0'));
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ }
+ while (*str++ != '\0');
+
+ return 0;
+}
+
+static int matchone(regex_t p, char c)
+{
+ switch (p.type)
+ {
+ case DOT: return matchdot(c);
+ case CHAR_CLASS: return matchcharclass(c, (const char*)p.ccl);
+ case INV_CHAR_CLASS: return !matchcharclass(c, (const char*)p.ccl);
+ case DIGIT: return matchdigit(c);
+ case NOT_DIGIT: return !matchdigit(c);
+ case ALPHA: return matchalphanum(c);
+ case NOT_ALPHA: return !matchalphanum(c);
+ case WHITESPACE: return matchwhitespace(c);
+ case NOT_WHITESPACE: return !matchwhitespace(c);
+ default: return (p.ch == c);
+ }
+}
+
+static int matchstar(regex_t p, regex_t* pattern, const char* text, int* matchlength)
+{
+ int prelen = *matchlength;
+ const char* prepoint = text;
+ while ((text[0] != '\0') && matchone(p, *text))
+ {
+ text++;
+ (*matchlength)++;
+ }
+ while (text >= prepoint)
+ {
+ if (matchpattern(pattern, text--, matchlength))
+ return 1;
+ (*matchlength)--;
+ }
+
+ *matchlength = prelen;
+ return 0;
+}
+
+static int matchplus(regex_t p, regex_t* pattern, const char* text, int* matchlength)
+{
+ const char* prepoint = text;
+ while ((text[0] != '\0') && matchone(p, *text))
+ {
+ text++;
+ (*matchlength)++;
+ }
+ while (text > prepoint)
+ {
+ if (matchpattern(pattern, text--, matchlength))
+ return 1;
+ (*matchlength)--;
+ }
+
+ return 0;
+}
+
+static int matchquestion(regex_t p, regex_t* pattern, const char* text, int* matchlength)
+{
+ if (p.type == UNUSED)
+ return 1;
+ if (matchpattern(pattern, text, matchlength))
+ return 1;
+ if (*text && matchone(p, *text++))
+ {
+ if (matchpattern(pattern, text, matchlength))
+ {
+ (*matchlength)++;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+#if 0
+
+/* Recursive matching */
+static int matchpattern(regex_t* pattern, const char* text, int *matchlength)
+{
+ int pre = *matchlength;
+ if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK))
+ {
+ return matchquestion(pattern[1], &pattern[2], text, matchlength);
+ }
+ else if (pattern[1].type == STAR)
+ {
+ return matchstar(pattern[0], &pattern[2], text, matchlength);
+ }
+ else if (pattern[1].type == PLUS)
+ {
+ return matchplus(pattern[0], &pattern[2], text, matchlength);
+ }
+ else if ((pattern[0].type == END) && pattern[1].type == UNUSED)
+ {
+ return text[0] == '\0';
+ }
+ else if ((text[0] != '\0') && matchone(pattern[0], text[0]))
+ {
+ (*matchlength)++;
+ return matchpattern(&pattern[1], text+1);
+ }
+ else
+ {
+ *matchlength = pre;
+ return 0;
+ }
+}
+
+#else
+
+/* Iterative matching */
+static int matchpattern(regex_t* pattern, const char* text, int* matchlength)
+{
+ int pre = *matchlength;
+ do
+ {
+ if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK))
+ {
+ return matchquestion(pattern[0], &pattern[2], text, matchlength);
+ }
+ else if (pattern[1].type == STAR)
+ {
+ return matchstar(pattern[0], &pattern[2], text, matchlength);
+ }
+ else if (pattern[1].type == PLUS)
+ {
+ return matchplus(pattern[0], &pattern[2], text, matchlength);
+ }
+ else if ((pattern[0].type == END) && pattern[1].type == UNUSED)
+ {
+ return (text[0] == '\0');
+ }
+/* Branching is not working properly
+ else if (pattern[1].type == BRANCH)
+ {
+ return (matchpattern(pattern, text) || matchpattern(&pattern[2], text));
+ }
+*/
+ (*matchlength)++;
+ }
+ while ((text[0] != '\0') && matchone(*pattern++, *text++));
+
+ *matchlength = pre;
+ return 0;
+}
+
+#endif
diff --git a/samples/12_c_extensions/02_intermediate/app/re.h b/samples/12_c_extensions/02_intermediate/app/re.h
new file mode 100644
index 0000000..fb2d002
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/app/re.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * Mini regex-module inspired by Rob Pike's regex code described in:
+ *
+ * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
+ *
+ *
+ *
+ * Supports:
+ * ---------
+ * '.' Dot, matches any character
+ * '^' Start anchor, matches beginning of string
+ * '$' End anchor, matches end of string
+ * '*' Asterisk, match zero or more (greedy)
+ * '+' Plus, match one or more (greedy)
+ * '?' Question, match zero or one (non-greedy)
+ * '[abc]' Character class, match if one of {'a', 'b', 'c'}
+ * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken!
+ * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z }
+ * '\s' Whitespace, \t \f \r \n \v and spaces
+ * '\S' Non-whitespace
+ * '\w' Alphanumeric, [a-zA-Z0-9_]
+ * '\W' Non-alphanumeric
+ * '\d' Digits, [0-9]
+ * '\D' Non-digits
+ *
+ *
+ */
+
+#ifndef _TINY_REGEX_C
+#define _TINY_REGEX_C
+
+
+#ifndef RE_DOT_MATCHES_NEWLINE
+/* Define to 0 if you DON'T want '.' to match '\r' + '\n' */
+#define RE_DOT_MATCHES_NEWLINE 1
+#endif
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+
+/* Typedef'd pointer to get abstract datatype. */
+typedef struct regex_t* re_t;
+
+
+/* Compile regex string pattern to a regex_t-array. */
+re_t re_compile(const char* pattern);
+
+
+/* Find matches of the compiled pattern inside text. */
+int re_matchp(re_t pattern, const char* text, int* matchlength);
+
+
+/* Find matches of the txt pattern inside text (will compile automatically first). */
+int re_match(const char* pattern, const char* text, int* matchlength);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ifndef _TINY_REGEX_C */
diff --git a/samples/12_c_extensions/02_intermediate/c-regex-demo.png b/samples/12_c_extensions/02_intermediate/c-regex-demo.png
new file mode 100644
index 0000000..19277c8
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/c-regex-demo.png
Binary files differ
diff --git a/samples/12_c_extensions/02_intermediate/license-for-regex-library.txt b/samples/12_c_extensions/02_intermediate/license-for-regex-library.txt
new file mode 100644
index 0000000..cf1ab25
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/license-for-regex-library.txt
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/samples/12_c_extensions/02_intermediate/license-for-sample.txt b/samples/12_c_extensions/02_intermediate/license-for-sample.txt
new file mode 100644
index 0000000..100dcec
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/license-for-sample.txt
@@ -0,0 +1,9 @@
+Copyright 2019 DragonRuby LLC
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/samples/12_c_extensions/02_intermediate/metadata/game_metadata.txt b/samples/12_c_extensions/02_intermediate/metadata/game_metadata.txt
new file mode 100644
index 0000000..80ed5d1
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/metadata/game_metadata.txt
@@ -0,0 +1,5 @@
+devid=dragonruby
+devtitle=DragonRuby LLC
+gameid=cintermediate
+gametitle=C Extensions Intermediate
+version=1.0
diff --git a/samples/12_c_extensions/02_intermediate/native/re-bindings.c b/samples/12_c_extensions/02_intermediate/native/re-bindings.c
new file mode 100644
index 0000000..6aa5630
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/native/re-bindings.c
@@ -0,0 +1,282 @@
+#include <mruby.h>
+#include <string.h>
+#include <assert.h>
+#include <mruby/string.h>
+#include <mruby/data.h>
+#include <dragonruby.h>
+#include "app/re.h"
+
+// MRuby `typedef`s mrb_int in the mruby/value.h
+// Then `#define`s mrb_int in mruby.h
+// We need to undo the macro and avoid it's usage
+// FIXME: I'm surely doing something wrong
+#ifdef mrb_int
+#undef mrb_int
+#endif
+
+static void (*drb_free_foreign_object_f)(mrb_state *, void *);
+static struct RClass *(*mrb_module_get_f)(mrb_state *, const char *);
+static mrb_int (*mrb_get_args_f)(mrb_state *, mrb_args_format, ...);
+static struct RClass *(*mrb_module_get_under_f)(mrb_state *, struct RClass *, const char *);
+static struct RClass *(*mrb_class_get_under_f)(mrb_state *, struct RClass *, const char *);
+static struct RClass *(*mrb_define_module_under_f)(mrb_state *, struct RClass *, const char *);
+static void (*mrb_define_module_function_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec);
+static struct RClass *(*mrb_define_class_under_f)(mrb_state *, struct RClass *, const char *, struct RClass *);
+static void (*mrb_define_method_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec);
+static void (*mrb_define_class_method_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec);
+static struct RData *(*mrb_data_object_alloc_f)(mrb_state *, struct RClass *, void *, const mrb_data_type *);
+static mrb_value (*mrb_str_new_cstr_f)(mrb_state *, const char *);
+static void (*mrb_raise_f)(mrb_state *, struct RClass *, const char *);
+static struct RClass *(*mrb_exc_get_f)(mrb_state *, const char *);
+static void drb_free_foreign_object_indirect(mrb_state *state, void *pointer) {
+ drb_free_foreign_object_f(state, pointer);
+}
+struct drb_foreign_object_ZTSP7regex_t {
+ drb_foreign_object_kind kind;
+ struct regex_t *value;
+ int should_free;
+};
+static mrb_data_type ForeignObjectType_ZTSP7regex_t = {"regex_t*", drb_free_foreign_object_indirect};
+static struct regex_t *drb_ffi__ZTSP7regex_t_FromRuby(mrb_state *state, mrb_value self) {
+ if (mrb_nil_p(self))
+ return 0;
+ return ((struct drb_foreign_object_ZTSP7regex_t *)DATA_PTR(self))->value;
+}
+static mrb_value drb_ffi__ZTSP7regex_t_ToRuby(mrb_state *state, struct regex_t *value) {
+ struct drb_foreign_object_ZTSP7regex_t *ptr = calloc(1, sizeof(struct drb_foreign_object_ZTSP7regex_t));
+ ptr->value = value;
+ ptr->kind = drb_foreign_object_kind_pointer;
+ struct RClass *FFI = mrb_module_get_f(state, "FFI");
+ struct RClass *module = mrb_module_get_under_f(state, FFI, "RE");
+ struct RClass *klass = mrb_class_get_under_f(state, module, "Regex_tPointer");
+ struct RData *rdata = mrb_data_object_alloc_f(state, klass, ptr, &ForeignObjectType_ZTSP7regex_t);
+ return mrb_obj_value(rdata);
+}
+struct drb_foreign_object_ZTSPc {
+ drb_foreign_object_kind kind;
+ char *value;
+ int should_free;
+};
+static mrb_data_type ForeignObjectType_ZTSPc = {"char*", drb_free_foreign_object_indirect};
+static char *drb_ffi__ZTSPc_FromRuby(mrb_state *state, mrb_value self) {
+ if (mrb_nil_p(self))
+ return 0;
+ if (mrb_type(self) == MRB_TT_STRING)
+ return RSTRING_PTR(self);
+ return ((struct drb_foreign_object_ZTSPc *)DATA_PTR(self))->value;
+}
+static mrb_value drb_ffi__ZTSPc_ToRuby(mrb_state *state, char *value) {
+ struct drb_foreign_object_ZTSPc *ptr = calloc(1, sizeof(struct drb_foreign_object_ZTSPc));
+ ptr->value = value;
+ ptr->kind = drb_foreign_object_kind_pointer;
+ struct RClass *FFI = mrb_module_get_f(state, "FFI");
+ struct RClass *module = mrb_module_get_under_f(state, FFI, "RE");
+ struct RClass *klass = mrb_class_get_under_f(state, module, "CharPointer");
+ struct RData *rdata = mrb_data_object_alloc_f(state, klass, ptr, &ForeignObjectType_ZTSPc);
+ return mrb_obj_value(rdata);
+}
+static int drb_ffi__ZTSi_FromRuby(mrb_state *state, mrb_value self) {
+ return mrb_fixnum(self);
+}
+static mrb_value drb_ffi__ZTSi_ToRuby(mrb_state *state, int value) {
+ return mrb_fixnum_value(value);
+}
+struct drb_foreign_object_ZTSPi {
+ drb_foreign_object_kind kind;
+ int *value;
+ int should_free;
+};
+static mrb_data_type ForeignObjectType_ZTSPi = {"int*", drb_free_foreign_object_indirect};
+static int *drb_ffi__ZTSPi_FromRuby(mrb_state *state, mrb_value self) {
+ if (mrb_nil_p(self))
+ return 0;
+ return ((struct drb_foreign_object_ZTSPi *)DATA_PTR(self))->value;
+}
+static mrb_value drb_ffi__ZTSPi_ToRuby(mrb_state *state, int *value) {
+ struct drb_foreign_object_ZTSPi *ptr = calloc(1, sizeof(struct drb_foreign_object_ZTSPi));
+ ptr->value = value;
+ ptr->kind = drb_foreign_object_kind_pointer;
+ struct RClass *FFI = mrb_module_get_f(state, "FFI");
+ struct RClass *module = mrb_module_get_under_f(state, FFI, "RE");
+ struct RClass *klass = mrb_class_get_under_f(state, module, "IntPointer");
+ struct RData *rdata = mrb_data_object_alloc_f(state, klass, ptr, &ForeignObjectType_ZTSPi);
+ return mrb_obj_value(rdata);
+}
+static char drb_ffi__ZTSc_FromRuby(mrb_state *state, mrb_value self) {
+ return mrb_fixnum(self);
+}
+static mrb_value drb_ffi__ZTSc_ToRuby(mrb_state *state, char value) {
+ return mrb_fixnum_value(value);
+}
+static mrb_value drb_ffi__ZTSP7regex_t_New(mrb_state *mrb, mrb_value self) {
+ mrb_raise_f(mrb, mrb_exc_get_f(mrb, "RuntimeError"), "Cannot allocate pointer of incomplete type");
+ return mrb_nil_value();
+}
+static mrb_value drb_ffi__ZTSP7regex_t_GetValue(mrb_state *mrb, mrb_value value) {
+ mrb_raise_f(mrb, mrb_exc_get_f(mrb, "RuntimeError"), "Cannot access value of incomplete type");
+ return mrb_nil_value();
+}
+static mrb_value drb_ffi__ZTSP7regex_t_IsNil(mrb_state *state, mrb_value self) {
+ if (drb_ffi__ZTSP7regex_t_FromRuby(state, self) == 0)
+ return mrb_true_value();
+ else
+ return mrb_false_value();
+}
+static mrb_value drb_ffi__ZTSP7regex_t_GetAt(mrb_state *mrb, mrb_value self) {
+ mrb_raise_f(mrb, mrb_exc_get_f(mrb, "RuntimeError"), "Cannot access value of incomplete type");
+ return mrb_nil_value();
+}
+static mrb_value drb_ffi__ZTSP7regex_t_SetAt(mrb_state *mrb, mrb_value self) {
+ mrb_raise_f(mrb, mrb_exc_get_f(mrb, "RuntimeError"), "Cannot change value of incomplete type");
+ return mrb_nil_value();
+}
+static mrb_value drb_ffi__ZTSPc_New(mrb_state *mrb, mrb_value self) {
+ struct drb_foreign_object_ZTSPc *ptr = calloc(1, sizeof(struct drb_foreign_object_ZTSPc));
+ ptr->kind = drb_foreign_object_kind_pointer;
+ ptr->value = calloc(1, sizeof(char));
+ ptr->should_free = 1;
+ struct RClass *FFI = mrb_module_get_f(mrb, "FFI");
+ struct RClass *module = mrb_module_get_under_f(mrb, FFI, "RE");
+ struct RClass *klass = mrb_class_get_under_f(mrb, module, "CharPointer");
+ struct RData *rdata = mrb_data_object_alloc_f(mrb, klass, ptr, &ForeignObjectType_ZTSPc);
+ return mrb_obj_value(rdata);
+}
+static mrb_value drb_ffi__ZTSPc_GetValue(mrb_state *mrb, mrb_value value) {
+ return drb_ffi__ZTSc_ToRuby(mrb, *drb_ffi__ZTSPc_FromRuby(mrb, value));
+}
+static mrb_value drb_ffi__ZTSPc_IsNil(mrb_state *state, mrb_value self) {
+ if (drb_ffi__ZTSPc_FromRuby(state, self) == 0)
+ return mrb_true_value();
+ else
+ return mrb_false_value();
+}
+static mrb_value drb_ffi__ZTSPc_GetAt(mrb_state *mrb, mrb_value self) {
+ mrb_value *args = 0;
+ mrb_int argc = 0;
+ mrb_get_args_f(mrb, "*", &args, &argc);
+ int index = drb_ffi__ZTSi_FromRuby(mrb, args[0]);
+ return drb_ffi__ZTSc_ToRuby(mrb, drb_ffi__ZTSPc_FromRuby(mrb, self)[index]);
+}
+static mrb_value drb_ffi__ZTSPc_SetAt(mrb_state *mrb, mrb_value self) {
+ mrb_value *args = 0;
+ mrb_int argc = 0;
+ mrb_get_args_f(mrb, "*", &args, &argc);
+ int index = drb_ffi__ZTSi_FromRuby(mrb, args[0]);
+ char new_value = drb_ffi__ZTSc_FromRuby(mrb, args[1]);
+ drb_ffi__ZTSPc_FromRuby(mrb, self)[index] = new_value;
+ return mrb_nil_value();
+}
+static mrb_value drb_ffi__ZTSPc_GetString(mrb_state *state, mrb_value self) {
+ return mrb_str_new_cstr_f(state, drb_ffi__ZTSPc_FromRuby(state, self));
+}
+static mrb_value drb_ffi__ZTSPi_New(mrb_state *mrb, mrb_value self) {
+ struct drb_foreign_object_ZTSPi *ptr = calloc(1, sizeof(struct drb_foreign_object_ZTSPi));
+ ptr->kind = drb_foreign_object_kind_pointer;
+ ptr->value = calloc(1, sizeof(int));
+ ptr->should_free = 1;
+ struct RClass *FFI = mrb_module_get_f(mrb, "FFI");
+ struct RClass *module = mrb_module_get_under_f(mrb, FFI, "RE");
+ struct RClass *klass = mrb_class_get_under_f(mrb, module, "IntPointer");
+ struct RData *rdata = mrb_data_object_alloc_f(mrb, klass, ptr, &ForeignObjectType_ZTSPi);
+ return mrb_obj_value(rdata);
+}
+static mrb_value drb_ffi__ZTSPi_GetValue(mrb_state *mrb, mrb_value value) {
+ return drb_ffi__ZTSi_ToRuby(mrb, *drb_ffi__ZTSPi_FromRuby(mrb, value));
+}
+static mrb_value drb_ffi__ZTSPi_IsNil(mrb_state *state, mrb_value self) {
+ if (drb_ffi__ZTSPi_FromRuby(state, self) == 0)
+ return mrb_true_value();
+ else
+ return mrb_false_value();
+}
+static mrb_value drb_ffi__ZTSPi_GetAt(mrb_state *mrb, mrb_value self) {
+ mrb_value *args = 0;
+ mrb_int argc = 0;
+ mrb_get_args_f(mrb, "*", &args, &argc);
+ int index = drb_ffi__ZTSi_FromRuby(mrb, args[0]);
+ return drb_ffi__ZTSi_ToRuby(mrb, drb_ffi__ZTSPi_FromRuby(mrb, self)[index]);
+}
+static mrb_value drb_ffi__ZTSPi_SetAt(mrb_state *mrb, mrb_value self) {
+ mrb_value *args = 0;
+ mrb_int argc = 0;
+ mrb_get_args_f(mrb, "*", &args, &argc);
+ int index = drb_ffi__ZTSi_FromRuby(mrb, args[0]);
+ int new_value = drb_ffi__ZTSi_FromRuby(mrb, args[1]);
+ drb_ffi__ZTSPi_FromRuby(mrb, self)[index] = new_value;
+ return mrb_nil_value();
+}
+static mrb_value drb_ffi_re_compile_Binding(mrb_state *state, mrb_value value) {
+ mrb_value *args = 0;
+ mrb_int argc = 0;
+ mrb_get_args_f(state, "*", &args, &argc);
+ char *pattern_0 = drb_ffi__ZTSPc_FromRuby(state, args[0]);
+ struct regex_t *ret_val = re_compile(pattern_0);
+ return drb_ffi__ZTSP7regex_t_ToRuby(state, ret_val);
+}
+static mrb_value drb_ffi_re_matchp_Binding(mrb_state *state, mrb_value value) {
+ mrb_value *args = 0;
+ mrb_int argc = 0;
+ mrb_get_args_f(state, "*", &args, &argc);
+ struct regex_t *pattern_0 = drb_ffi__ZTSP7regex_t_FromRuby(state, args[0]);
+ char *text_1 = drb_ffi__ZTSPc_FromRuby(state, args[1]);
+ int *matchlength_2 = drb_ffi__ZTSPi_FromRuby(state, args[2]);
+ int ret_val = re_matchp(pattern_0, text_1, matchlength_2);
+ return drb_ffi__ZTSi_ToRuby(state, ret_val);
+}
+static mrb_value drb_ffi_re_match_Binding(mrb_state *state, mrb_value value) {
+ mrb_value *args = 0;
+ mrb_int argc = 0;
+ mrb_get_args_f(state, "*", &args, &argc);
+ char *pattern_0 = drb_ffi__ZTSPc_FromRuby(state, args[0]);
+ char *text_1 = drb_ffi__ZTSPc_FromRuby(state, args[1]);
+ int *matchlength_2 = drb_ffi__ZTSPi_FromRuby(state, args[2]);
+ int ret_val = re_match(pattern_0, text_1, matchlength_2);
+ return drb_ffi__ZTSi_ToRuby(state, ret_val);
+}
+static int drb_ffi_init_indirect_functions(void *(*lookup)(const char *));
+DRB_FFI_EXPORT
+void drb_register_c_extensions(void *(*lookup)(const char *), mrb_state *state, struct RClass *FFI) {
+ if (drb_ffi_init_indirect_functions(lookup))
+ return;
+ struct RClass *module = mrb_define_module_under_f(state, FFI, "RE");
+ struct RClass *object_class = state->object_class;
+ mrb_define_module_function_f(state, module, "re_compile", drb_ffi_re_compile_Binding, MRB_ARGS_REQ(1));
+ mrb_define_module_function_f(state, module, "re_matchp", drb_ffi_re_matchp_Binding, MRB_ARGS_REQ(3));
+ mrb_define_module_function_f(state, module, "re_match", drb_ffi_re_match_Binding, MRB_ARGS_REQ(3));
+ struct RClass *Regex_tPointerClass = mrb_define_class_under_f(state, module, "Regex_tPointer", object_class);
+ mrb_define_class_method_f(state, Regex_tPointerClass, "new", drb_ffi__ZTSP7regex_t_New, MRB_ARGS_REQ(0));
+ mrb_define_method_f(state, Regex_tPointerClass, "value", drb_ffi__ZTSP7regex_t_GetValue, MRB_ARGS_REQ(0));
+ mrb_define_method_f(state, Regex_tPointerClass, "[]", drb_ffi__ZTSP7regex_t_GetAt, MRB_ARGS_REQ(1));
+ mrb_define_method_f(state, Regex_tPointerClass, "[]=", drb_ffi__ZTSP7regex_t_SetAt, MRB_ARGS_REQ(2));
+ mrb_define_method_f(state, Regex_tPointerClass, "nil?", drb_ffi__ZTSP7regex_t_IsNil, MRB_ARGS_REQ(0));
+ struct RClass *CharPointerClass = mrb_define_class_under_f(state, module, "CharPointer", object_class);
+ mrb_define_class_method_f(state, CharPointerClass, "new", drb_ffi__ZTSPc_New, MRB_ARGS_REQ(0));
+ mrb_define_method_f(state, CharPointerClass, "value", drb_ffi__ZTSPc_GetValue, MRB_ARGS_REQ(0));
+ mrb_define_method_f(state, CharPointerClass, "[]", drb_ffi__ZTSPc_GetAt, MRB_ARGS_REQ(1));
+ mrb_define_method_f(state, CharPointerClass, "[]=", drb_ffi__ZTSPc_SetAt, MRB_ARGS_REQ(2));
+ mrb_define_method_f(state, CharPointerClass, "nil?", drb_ffi__ZTSPc_IsNil, MRB_ARGS_REQ(0));
+ mrb_define_method_f(state, CharPointerClass, "str", drb_ffi__ZTSPc_GetString, MRB_ARGS_REQ(0));
+ struct RClass *IntPointerClass = mrb_define_class_under_f(state, module, "IntPointer", object_class);
+ mrb_define_class_method_f(state, IntPointerClass, "new", drb_ffi__ZTSPi_New, MRB_ARGS_REQ(0));
+ mrb_define_method_f(state, IntPointerClass, "value", drb_ffi__ZTSPi_GetValue, MRB_ARGS_REQ(0));
+ mrb_define_method_f(state, IntPointerClass, "[]", drb_ffi__ZTSPi_GetAt, MRB_ARGS_REQ(1));
+ mrb_define_method_f(state, IntPointerClass, "[]=", drb_ffi__ZTSPi_SetAt, MRB_ARGS_REQ(2));
+ mrb_define_method_f(state, IntPointerClass, "nil?", drb_ffi__ZTSPi_IsNil, MRB_ARGS_REQ(0));
+}
+static int drb_ffi_init_indirect_functions(void *(*lookup)(const char *fnname)) {
+ if (!(mrb_raise_f = (void (*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_raise"))) return -1;
+ if (!(mrb_str_new_cstr_f = (mrb_value (*)(mrb_state *, const char *)) lookup("mrb_str_new_cstr"))) return -1;
+ if (!(mrb_define_class_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *, struct RClass *)) lookup("mrb_define_class_under"))) return -1;
+ if (!(mrb_define_class_method_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_class_method"))) return -1;
+ if (!(mrb_class_get_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_class_get_under"))) return -1;
+ if (!(mrb_module_get_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_module_get_under"))) return -1;
+ if (!(drb_free_foreign_object_f = (void (*)(mrb_state *, void *)) lookup("drb_free_foreign_object"))) return -1;
+ if (!(mrb_data_object_alloc_f = (struct RData *(*)(mrb_state *, struct RClass *, void *, const mrb_data_type *)) lookup("mrb_data_object_alloc"))) return -1;
+ if (!(mrb_get_args_f = (mrb_int (*)(mrb_state *, mrb_args_format, ...)) lookup("mrb_get_args"))) return -1;
+ if (!(mrb_module_get_f = (struct RClass *(*)(mrb_state *, const char *)) lookup("mrb_module_get"))) return -1;
+ if (!(mrb_define_method_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_method"))) return -1;
+ if (!(mrb_exc_get_f = (struct RClass *(*)(mrb_state *, const char *)) lookup("mrb_exc_get"))) return -1;
+ if (!(mrb_define_module_function_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_module_function"))) return -1;
+ if (!(mrb_define_module_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_define_module_under"))) return -1;
+ return 0;
+}
diff --git a/samples/12_c_extensions/02_intermediate/pre.bat b/samples/12_c_extensions/02_intermediate/pre.bat
new file mode 100644
index 0000000..2b4ea2e
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/pre.bat
@@ -0,0 +1,5 @@
+set DRB_ROOT=..\..\..\
+md native
+md native\windows-amd64
+%DRB_ROOT%\dragonruby-bind.exe --ffi-module=RE --output=native\re-bindings.c app\re.h
+clang -shared .\native\re-bindings.c .\app\re.c --sysroot=C:\mingw-w64\mingw64 --target=x86_64-w64-mingw32 -fuse-ld=lld -isystem %DRB_ROOT%\include -I. -o native\windows-amd64\ext.dll
diff --git a/samples/12_c_extensions/02_intermediate/pre.sh b/samples/12_c_extensions/02_intermediate/pre.sh
new file mode 100755
index 0000000..405f205
--- /dev/null
+++ b/samples/12_c_extensions/02_intermediate/pre.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+OSTYPE=`uname -s`
+if [ "x$OSTYPE" = "xDarwin" ]; then
+ PLATFORM=macos
+ DLLEXT=dylib
+else
+ PLATFORM=linux-amd64
+ DLLEXT=so
+fi
+
+DRB_ROOT=../../..
+mkdir -p native/$PLATFORM
+
+$DRB_ROOT/dragonruby-bind -ffi-module=RE --output=native/re-bindings.c app/re.h
+clang \
+ -isystem $DRB_ROOT/include -I. \
+ -fPIC -shared app/re.c native/re-bindings.c -o native/$PLATFORM/ext.$DLLEXT
+