xrb 0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/bake/xrb/entities.rb +60 -0
  4. data/bake/xrb/parsers.rb +69 -0
  5. data/ext/extconf.rb +21 -0
  6. data/ext/xrb/escape.c +152 -0
  7. data/ext/xrb/escape.h +15 -0
  8. data/ext/xrb/markup.c +1949 -0
  9. data/ext/xrb/markup.h +6 -0
  10. data/ext/xrb/markup.rl +226 -0
  11. data/ext/xrb/query.c +619 -0
  12. data/ext/xrb/query.h +6 -0
  13. data/ext/xrb/query.rl +82 -0
  14. data/ext/xrb/tag.c +204 -0
  15. data/ext/xrb/tag.h +21 -0
  16. data/ext/xrb/template.c +1114 -0
  17. data/ext/xrb/template.h +6 -0
  18. data/ext/xrb/template.rl +77 -0
  19. data/ext/xrb/xrb.c +72 -0
  20. data/ext/xrb/xrb.h +132 -0
  21. data/lib/xrb/buffer.rb +103 -0
  22. data/lib/xrb/builder.rb +222 -0
  23. data/lib/xrb/entities.rb +2137 -0
  24. data/lib/xrb/entities.xrb +30 -0
  25. data/lib/xrb/error.rb +81 -0
  26. data/lib/xrb/fallback/markup.rb +1658 -0
  27. data/lib/xrb/fallback/markup.rl +228 -0
  28. data/lib/xrb/fallback/query.rb +548 -0
  29. data/lib/xrb/fallback/query.rl +88 -0
  30. data/lib/xrb/fallback/template.rb +829 -0
  31. data/lib/xrb/fallback/template.rl +80 -0
  32. data/lib/xrb/markup.rb +56 -0
  33. data/lib/xrb/native.rb +15 -0
  34. data/lib/xrb/parse_delegate.rb +19 -0
  35. data/lib/xrb/parsers.rb +17 -0
  36. data/lib/xrb/query.rb +80 -0
  37. data/lib/xrb/reference.rb +108 -0
  38. data/lib/xrb/strings.rb +47 -0
  39. data/lib/xrb/tag.rb +115 -0
  40. data/lib/xrb/template.rb +164 -0
  41. data/lib/xrb/uri.rb +100 -0
  42. data/lib/xrb/version.rb +8 -0
  43. data/lib/xrb.rb +11 -0
  44. data/license.md +23 -0
  45. data/readme.md +29 -0
  46. data.tar.gz.sig +0 -0
  47. metadata +109 -58
  48. metadata.gz.sig +0 -0
  49. data/README +0 -60
  50. data/app/helpers/ui_helper.rb +0 -80
  51. data/app/models/xrb/element.rb +0 -9
  52. data/lib/xrb/engine.rb +0 -4
  53. data/rails/init.rb +0 -1
  54. data/xrb.gemspec +0 -12
@@ -0,0 +1,6 @@
1
+
2
+ #pragma once
3
+
4
+ #include "xrb.h"
5
+
6
+ VALUE XRB_Native_parse_template(VALUE self, VALUE buffer, VALUE delegate);
@@ -0,0 +1,77 @@
1
+
2
+ #include "template.h"
3
+
4
+ %%{
5
+ machine XRB_template_parser;
6
+
7
+ action instruction_begin {
8
+ instruction.begin = p;
9
+ }
10
+
11
+ action instruction_end {
12
+ instruction.end = p;
13
+ }
14
+
15
+ action emit_instruction {
16
+ rb_funcall(delegate, id_instruction, 1, XRB_Token_string(instruction, encoding));
17
+ }
18
+
19
+ action emit_instruction_line {
20
+ rb_funcall(delegate, id_instruction, 2, XRB_Token_string(instruction, encoding), newline);
21
+ }
22
+
23
+ action instruction_error {
24
+ XRB_raise_error("failed to parse instruction", buffer, p-s);
25
+ }
26
+
27
+ action expression_begin {
28
+ expression.begin = p;
29
+ }
30
+
31
+ action expression_end {
32
+ expression.end = p;
33
+ }
34
+
35
+ action emit_expression {
36
+ rb_funcall(delegate, id_expression, 1, XRB_Token_string(expression, encoding));
37
+ }
38
+
39
+ action expression_error {
40
+ XRB_raise_error("failed to parse expression", buffer, p-s);
41
+ }
42
+
43
+ action emit_text {
44
+ rb_funcall(delegate, id_text, 1, XRB_string(ts, te, encoding));
45
+ }
46
+
47
+ include template "xrb/template.rl";
48
+
49
+ write data;
50
+ }%%
51
+
52
+ VALUE XRB_Native_parse_template(VALUE self, VALUE buffer, VALUE delegate) {
53
+ VALUE string = rb_funcall(buffer, id_read, 0);
54
+
55
+ rb_encoding *encoding = rb_enc_get(string);
56
+
57
+ VALUE newline = rb_obj_freeze(rb_enc_str_new("\n", 1, encoding));
58
+
59
+ const char *s, *p, *pe, *eof, *ts, *te;
60
+ unsigned long cs, act, top = 0, stack[32] = {0};
61
+
62
+ XRB_Token expression = {0}, instruction = {0};
63
+
64
+ s = p = RSTRING_PTR(string);
65
+ eof = pe = p + RSTRING_LEN(string);
66
+
67
+ %%{
68
+ write init;
69
+ write exec;
70
+ }%%
71
+
72
+ if (p != eof) {
73
+ XRB_raise_error("could not parse all input", buffer, p-s);
74
+ }
75
+
76
+ return Qnil;
77
+ }
data/ext/xrb/xrb.c ADDED
@@ -0,0 +1,72 @@
1
+
2
+ #include "xrb.h"
3
+
4
+ #include "markup.h"
5
+ #include "template.h"
6
+ #include "query.h"
7
+ #include "tag.h"
8
+ #include "escape.h"
9
+
10
+ VALUE rb_XRB = Qnil, rb_XRB_Native = Qnil, rb_XRB_Tag = Qnil, rb_XRB_Markup = Qnil, rb_XRB_MarkupString = Qnil, rb_XRB_ParseError = Qnil;
11
+ ID id_cdata, id_open_tag_begin, id_open_tag_end, id_attribute, id_close_tag, id_text, id_doctype, id_comment, id_instruction, id_read, id_expression, id_key_get, id_string, id_integer, id_append, id_assign, id_pair, id_new, id_name, id_attributes, id_closed, id_to_s, id_is_a;
12
+
13
+ void XRB_raise_error(const char * message, VALUE buffer, size_t offset) {
14
+ VALUE exception = rb_funcall(rb_XRB_ParseError, id_new, 3, rb_str_new_cstr(message), buffer, ULONG2NUM(offset));
15
+
16
+ rb_exc_raise(exception);
17
+ }
18
+
19
+ void Init_XRB_Extension() {
20
+ id_open_tag_begin = rb_intern("open_tag_begin");
21
+ id_open_tag_end = rb_intern("open_tag_end");
22
+ id_close_tag = rb_intern("close_tag");
23
+
24
+ id_cdata = rb_intern("cdata");
25
+ id_attribute = rb_intern("attribute");
26
+ id_comment = rb_intern("comment");
27
+ id_text = rb_intern("text");
28
+ id_doctype = rb_intern("doctype");
29
+ id_instruction = rb_intern("instruction");
30
+ id_expression = rb_intern("expression");
31
+
32
+ id_read = rb_intern("read");
33
+ id_new = rb_intern("new");
34
+
35
+ id_name = rb_intern("name");
36
+ id_attributes = rb_intern("attributes");
37
+ id_closed = rb_intern("closed");
38
+
39
+ id_key_get = rb_intern("[]");
40
+
41
+ id_string = rb_intern("string");
42
+ id_integer = rb_intern("integer");
43
+ id_append = rb_intern("append");
44
+ id_assign = rb_intern("assign");
45
+ id_pair = rb_intern("pair");
46
+
47
+ id_to_s = rb_intern("to_s");
48
+ id_is_a = rb_intern("is_a?");
49
+
50
+ rb_XRB = rb_define_module("XRB");
51
+ rb_gc_register_mark_object(rb_XRB);
52
+
53
+ rb_XRB_Markup = rb_define_module_under(rb_XRB, "Markup");
54
+ rb_gc_register_mark_object(rb_XRB_Markup);
55
+
56
+ rb_XRB_Native = rb_define_module_under(rb_XRB, "Native");
57
+ rb_gc_register_mark_object(rb_XRB_Native);
58
+
59
+ Init_xrb_escape();
60
+
61
+ rb_XRB_ParseError = rb_const_get_at(rb_XRB, rb_intern("ParseError"));
62
+ rb_gc_register_mark_object(rb_XRB_ParseError);
63
+
64
+ rb_define_module_function(rb_XRB_Native, "parse_markup", XRB_Native_parse_markup, 3);
65
+ rb_define_module_function(rb_XRB_Native, "parse_template", XRB_Native_parse_template, 2);
66
+ rb_define_module_function(rb_XRB_Native, "parse_query", XRB_Native_parse_query, 2);
67
+
68
+ rb_XRB_Tag = rb_const_get_at(rb_XRB, rb_intern("Tag"));
69
+ rb_gc_register_mark_object(rb_XRB_Tag);
70
+
71
+ Init_xrb_tag();
72
+ }
data/ext/xrb/xrb.h ADDED
@@ -0,0 +1,132 @@
1
+
2
+ #pragma once
3
+
4
+ #include "ruby.h"
5
+ #include <ruby/encoding.h>
6
+
7
+ // Used to efficiently convert symbols to strings (e.g. tag attribute keys).
8
+ #ifndef HAVE_RB_SYM2STR
9
+ #define rb_sym2str(sym) rb_id2str(SYM2ID(sym))
10
+ #endif
11
+
12
+ // Consistent and meaningful append cstring to ruby string/buffer.
13
+ #ifndef HAVE_RB_STR_CAT_CSTR
14
+ #define rb_str_cat_cstr rb_str_cat2
15
+ #endif
16
+
17
+ // Prefer non-generic macro names where possible.
18
+ #ifndef RB_IMMEDIATE_P
19
+ #define RB_IMMEDIATE_P IMMEDIATE_P
20
+ #endif
21
+
22
+ // A helper to reserve a specific capacity of data for a buffer.
23
+ #ifndef HAVE_RB_STR_RESERVE
24
+ inline VALUE rb_str_reserve(VALUE string, long extra) {
25
+ long actual = RSTRING_LEN(string);
26
+ rb_str_resize(string, actual + extra);
27
+ rb_str_set_len(string, actual);
28
+ return string;
29
+ }
30
+ #endif
31
+
32
+ // Modules and classes exposed by XRB.
33
+ extern VALUE
34
+ rb_XRB,
35
+ rb_XRB_Markup,
36
+ rb_XRB_Tag,
37
+ rb_XRB_MarkupString,
38
+ rb_XRB_Native,
39
+ rb_XRB_ParseError;
40
+
41
+ // Symbols used for delegate callbacks and general function calls.
42
+ extern ID
43
+ id_cdata,
44
+ id_open_tag_begin,
45
+ id_open_tag_end,
46
+ id_attribute,
47
+ id_close_tag,
48
+ id_text,
49
+ id_doctype,
50
+ id_comment,
51
+ id_instruction,
52
+ id_read,
53
+ id_expression,
54
+ id_key_get,
55
+ id_new,
56
+ id_name,
57
+ id_integer,
58
+ id_string,
59
+ id_append,
60
+ id_assign,
61
+ id_pair,
62
+ id_attributes,
63
+ id_closed,
64
+ id_to_s,
65
+ id_is_a;
66
+
67
+ // A convenient C string token class.
68
+ typedef struct {
69
+ const char * begin;
70
+ const char * end;
71
+ } XRB_Token;
72
+
73
+ // Convert a token to a Ruby string.
74
+ static inline VALUE XRB_Token_string(XRB_Token token, rb_encoding * encoding) {
75
+ return rb_enc_str_new(token.begin, token.end - token.begin, encoding);
76
+ }
77
+
78
+ // Convert a C string to a Ruby string.
79
+ static inline VALUE XRB_string(const char * begin, const char * end, rb_encoding * encoding) {
80
+ return rb_enc_str_new(begin, end - begin, encoding);
81
+ }
82
+
83
+ // Create an empty buffer for the given input string.
84
+ static inline VALUE XRB_buffer_for(VALUE string) {
85
+ VALUE buffer = rb_enc_str_new(0, 0, rb_enc_get(string));
86
+
87
+ rb_str_reserve(buffer, RSTRING_LEN(string) + 128);
88
+
89
+ return buffer;
90
+ }
91
+
92
+ // Raise a parse error for the given input buffer at a specific offset.
93
+ NORETURN(void XRB_raise_error(const char * message, VALUE buffer, size_t offset));
94
+
95
+ // Append a string to a buffer. The buffer may or may not be initialized.
96
+ static inline void XRB_append(VALUE * buffer, rb_encoding * encoding, VALUE string) {
97
+ if (*buffer == Qnil) {
98
+ *buffer = rb_enc_str_new(0, 0, encoding);
99
+ }
100
+
101
+ rb_str_concat(*buffer, string);
102
+ }
103
+
104
+ // Append a token to a buffer. The buffer may or may not be initialized.
105
+ static inline void XRB_append_token(VALUE * buffer, rb_encoding * encoding, XRB_Token token) {
106
+ if (*buffer == Qnil) {
107
+ // Allocate a buffer exactly the right size:
108
+ *buffer = rb_enc_str_new(token.begin, token.end - token.begin, encoding);
109
+ } else {
110
+ // Append the characters to the existing buffer:
111
+ rb_str_buf_cat(*buffer, token.begin, token.end - token.begin);
112
+ }
113
+ }
114
+
115
+ // Append a (unicode) codepoint to a buffer. The buffer may or may not be initialized.
116
+ static inline void XRB_append_codepoint(VALUE * buffer, rb_encoding * encoding, unsigned long codepoint) {
117
+ if (*buffer == Qnil) {
118
+ *buffer = rb_enc_str_new(0, 0, encoding);
119
+ }
120
+
121
+ rb_str_concat(*buffer, ULONG2NUM(codepoint));
122
+ }
123
+
124
+ // Convert the class of a string if there were no entities detected.
125
+ static inline VALUE XRB_markup_safe(VALUE string, unsigned has_entities) {
126
+ if (!has_entities) {
127
+ // Apparently should not use this to change klass, but it's exactly what we need here to make things lightning fast.
128
+ rb_obj_reveal(string, rb_XRB_MarkupString);
129
+ }
130
+
131
+ return string;
132
+ }
data/lib/xrb/buffer.rb ADDED
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2016-2024, by Samuel Williams.
5
+
6
+ module XRB
7
+ class Buffer
8
+ def initialize(string, path: '<string>')
9
+ @string = string
10
+ @path = path
11
+ end
12
+
13
+ attr :path
14
+
15
+ def encoding
16
+ @string.encoding
17
+ end
18
+
19
+ def read
20
+ @string
21
+ end
22
+
23
+ def self.load_file(path)
24
+ FileBuffer.new(path).freeze
25
+ end
26
+
27
+ def self.load(string)
28
+ Buffer.new(string).freeze
29
+ end
30
+
31
+ def to_buffer
32
+ self
33
+ end
34
+ end
35
+
36
+ class FileBuffer
37
+ def initialize(path)
38
+ @path = path
39
+ end
40
+
41
+ def freeze
42
+ return self if frozen?
43
+
44
+ read
45
+
46
+ super
47
+ end
48
+
49
+ attr :path
50
+
51
+ def encoding
52
+ read.encoding
53
+ end
54
+
55
+ def read
56
+ @cache ||= File.read(@path).freeze
57
+ end
58
+
59
+ def to_buffer
60
+ Buffer.new(self.read, @path)
61
+ end
62
+ end
63
+
64
+ class IOBuffer
65
+ def initialize(io, path: io.inspect)
66
+ @io = io
67
+ @path = path
68
+ end
69
+
70
+ def freeze
71
+ return self if frozen?
72
+
73
+ read
74
+
75
+ super
76
+ end
77
+
78
+ attr :path
79
+
80
+ def encoding
81
+ read.encoding
82
+ end
83
+
84
+ def read
85
+ @cache ||= @io.read.freeze
86
+ end
87
+
88
+ def to_buffer
89
+ Buffer.new(self.read, path: @path)
90
+ end
91
+ end
92
+
93
+ def self.Buffer(value)
94
+ case value
95
+ when String
96
+ Buffer.new(value)
97
+ when Buffer, FileBuffer, IOBuffer
98
+ value
99
+ else
100
+ value.to_buffer
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,222 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2012-2024, by Samuel Williams.
5
+
6
+ require_relative 'markup'
7
+ require_relative 'tag'
8
+
9
+ module XRB
10
+ # Build markup quickly and efficiently.
11
+ class Builder
12
+ include Markup
13
+
14
+ INDENT = "\t"
15
+
16
+ class Fragment
17
+ def initialize(block)
18
+ @block = block
19
+ @builder = nil
20
+ end
21
+
22
+ def call(builder)
23
+ @block.call(builder)
24
+ end
25
+
26
+ def to_s
27
+ unless @builder
28
+ @builder = Builder.new
29
+
30
+ self.call(@builder)
31
+ end
32
+
33
+ return @builder.to_s
34
+ end
35
+
36
+ def == other
37
+ # This is a bit of a hack... but is required for existing specs to pass:
38
+ self.to_s == other.to_s
39
+ end
40
+ end
41
+
42
+ # A helper to generate fragments of markup.
43
+ def self.fragment(output = nil, &block)
44
+ if output.is_a?(Binding)
45
+ output = Template.buffer(output)
46
+ end
47
+
48
+ if output.nil?
49
+ return Fragment.new(block)
50
+ end
51
+
52
+ if output.is_a?(Builder)
53
+ block.call(output)
54
+ else
55
+ block.call(Builder.new(output))
56
+ end
57
+
58
+ return nil
59
+ end
60
+
61
+ def self.tag(name, content, **attributes)
62
+ self.fragment do |builder|
63
+ builder.inline(name, attributes) do
64
+ builder.text(content)
65
+ end
66
+ end
67
+ end
68
+
69
+ def initialize(output = nil, indent: true, encoding: Encoding::UTF_8)
70
+ # This field gets togged in #inline so we keep track of it separately from @indentation.
71
+ @indent = indent
72
+
73
+ # We don't need to use MarkupString here as Builder itself is considered markup and should be inserted directly into the output stream.
74
+ @output = output || MarkupString.new.force_encoding(encoding)
75
+
76
+ @level = [0]
77
+ @children = [0]
78
+ end
79
+
80
+ attr :output
81
+
82
+ def encoding
83
+ @output.encoding
84
+ end
85
+
86
+ # Required for output to buffer.
87
+ def to_str
88
+ @output
89
+ end
90
+
91
+ alias to_s to_str
92
+
93
+ def == other
94
+ @output == String(other)
95
+ end
96
+
97
+ def indentation
98
+ if @indent
99
+ INDENT * (@level.size - 1)
100
+ else
101
+ ''
102
+ end
103
+ end
104
+
105
+ def doctype(attributes = 'html')
106
+ @output << "<!DOCTYPE #{attributes}>\n"
107
+ end
108
+
109
+ # Begin a block tag.
110
+ def tag(name, attributes = {}, &block)
111
+ full_tag(name, attributes, @indent, @indent, &block)
112
+ end
113
+
114
+ # Begin an inline tag.
115
+ def inline_tag(name, attributes = {}, &block)
116
+ original_indent = @indent
117
+
118
+ full_tag(name, attributes, @indent, false) do
119
+ @indent = false
120
+ yield if block_given?
121
+ end
122
+ ensure
123
+ @indent = original_indent
124
+ end
125
+
126
+ alias inline inline_tag
127
+
128
+ def inline!
129
+ original_indent = @indent
130
+ @indent = false
131
+
132
+ yield
133
+ ensure
134
+ @indent = original_indent
135
+ end
136
+
137
+ def text(content)
138
+ return unless content
139
+
140
+ if @indent
141
+ @output << "\n" if @level.last > 0
142
+ @output << indentation
143
+ end
144
+
145
+ Markup.append(@output, content)
146
+
147
+ if @indent
148
+ @output << "\n"
149
+ end
150
+ end
151
+
152
+ def raw(content)
153
+ @output << content
154
+ end
155
+
156
+ def <<(content)
157
+ return unless content
158
+
159
+ if content.is_a?(Fragment)
160
+ inline! do
161
+ content.call(self)
162
+ end
163
+ else
164
+ Markup.append(@output, content)
165
+ end
166
+ end
167
+
168
+ # Append pre-existing markup:
169
+ def append(value)
170
+ return unless value
171
+
172
+ # The parent has one more child:
173
+ @level[-1] += 1
174
+
175
+ if @indent
176
+ value.each_line.with_index do |line, i|
177
+ @output << indentation << line
178
+ end
179
+ else
180
+ @output << value
181
+ end
182
+ end
183
+
184
+ protected
185
+
186
+ # A normal block level/container tag.
187
+ def full_tag(name, attributes, indent_outer, indent_inner, &block)
188
+ if block_given?
189
+ if indent_outer
190
+ @output << "\n" if @level.last > 0
191
+ @output << indentation
192
+ end
193
+
194
+ tag = XRB::Tag.opened(name.to_s, attributes)
195
+ tag.write_opening_tag(@output)
196
+ @output << "\n" if indent_inner
197
+
198
+ # The parent has one more child:
199
+ @level[-1] += 1
200
+
201
+ @level << 0
202
+
203
+ yield
204
+
205
+ children = @level.pop
206
+
207
+ if indent_inner
208
+ @output << "\n" if children > 0
209
+ @output << indentation
210
+ end
211
+
212
+ tag.write_closing_tag(@output)
213
+ else
214
+ # The parent has one more child:
215
+ @level[-1] += 1
216
+
217
+ @output << indentation
218
+ XRB::Tag.append_tag(@output, name.to_s, attributes, nil)
219
+ end
220
+ end
221
+ end
222
+ end