1pub const RUNTIME: &'static str = r#"
2#include "ruby.h"
3#include <stdbool.h>
4#include <string.h>
5
6static const char *DOT = ".";
7
8static ID id_escape_html;
9static ID id_key_p;
10static ID id_to_s;
11
12static VALUE cCGI;
13
14static VALUE fetch(VALUE context, VALUE key) {
15 if (RSTRING_LEN(key) == 1 && strncmp(StringValuePtr(key), DOT, 1) == 0) {
16 return context;
17 }
18
19 switch (rb_type(context)) {
20 case T_HASH:
21 if (RTEST(rb_funcall(context, id_key_p, 1, key))) {
22 return rb_hash_aref(context, key);
23 } else {
24 return Qundef;
25 }
26 case T_STRUCT:
27 // TODO Check rb_struct_members to avoid name error.
28 return rb_struct_aref(context, key);
29 case T_OBJECT: {
30 ID method = rb_to_id(key);
31 if (rb_respond_to(context, method) && rb_obj_method_arity(context, method) == 0) {
32 return rb_funcall(context, method, 0);
33 } else {
34 return Qundef;
35 }
36 }
37 default:
38 return Qundef;
39 }
40}
41
42static VALUE context_fetch(VALUE stack, VALUE key) {
43 for (long i = RARRAY_LEN(stack) - 1; i >= 0; i--) {
44 VALUE context = rb_ary_entry(stack, i);
45 VALUE value = fetch(context, key);
46 if (value != Qundef) {
47 return value;
48 }
49 }
50 return Qnil;
51}
52
53static VALUE fetch_path(VALUE stack, VALUE path) {
54 VALUE value = context_fetch(stack, rb_ary_entry(path, 0));
55 if (value == Qnil) {
56 return Qnil;
57 }
58 for (long i = 1; i < RARRAY_LEN(path); i++) {
59 value = fetch(value, rb_ary_entry(path, i));
60 if (value == Qundef) {
61 return Qnil;
62 }
63 }
64 return value;
65}
66
67static void append_value(VALUE buf, VALUE stack, VALUE path, bool escape) {
68 VALUE value = fetch_path(stack, path);
69 switch (rb_type(value)) {
70 case T_NIL:
71 case T_UNDEF:
72 return;
73 case T_STRING:
74 break;
75 default:
76 value = rb_funcall(value, id_to_s, 0);
77 break;
78 }
79 rb_str_buf_append(buf, escape ? rb_funcall(cCGI, id_escape_html, 1, value) : value);
80}
81
82static void section(VALUE buf, VALUE stack, VALUE path, void (*block)(VALUE, VALUE)) {
83 VALUE value = fetch_path(stack, path);
84 switch (rb_type(value)) {
85 case T_ARRAY:
86 for (long i = 0; i < RARRAY_LEN(value); i++) {
87 rb_ary_push(stack, rb_ary_entry(value, i));
88 block(buf, stack);
89 rb_ary_pop(stack);
90 }
91 break;
92 case T_HASH:
93 case T_OBJECT:
94 case T_STRUCT:
95 rb_ary_push(stack, value);
96 block(buf, stack);
97 rb_ary_pop(stack);
98 break;
99 case T_NIL:
100 case T_UNDEF:
101 case T_FALSE:
102 break;
103 default:
104 block(buf, stack);
105 break;
106 }
107}
108
109static void inverted(VALUE buf, VALUE stack, VALUE path, void (*block)(VALUE, VALUE)) {
110 VALUE value = fetch_path(stack, path);
111 switch (rb_type(value)) {
112 case T_ARRAY:
113 if (RARRAY_LEN(value) == 0) {
114 block(buf, stack);
115 }
116 break;
117 case T_NIL:
118 case T_UNDEF:
119 case T_FALSE:
120 block(buf, stack);
121 break;
122 }
123}
124"#;