markly 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/bin/markly +94 -0
  3. data/ext/markly/arena.c +103 -0
  4. data/ext/markly/autolink.c +425 -0
  5. data/ext/markly/autolink.h +8 -0
  6. data/ext/markly/blocks.c +1585 -0
  7. data/ext/markly/buffer.c +278 -0
  8. data/ext/markly/buffer.h +116 -0
  9. data/ext/markly/case_fold_switch.inc +4327 -0
  10. data/ext/markly/chunk.h +135 -0
  11. data/ext/markly/cmark-gfm-core-extensions.h +54 -0
  12. data/ext/markly/cmark-gfm-extension_api.h +736 -0
  13. data/ext/markly/cmark-gfm-extensions_export.h +42 -0
  14. data/ext/markly/cmark-gfm.h +817 -0
  15. data/ext/markly/cmark-gfm_export.h +42 -0
  16. data/ext/markly/cmark-gfm_version.h +7 -0
  17. data/ext/markly/cmark.c +55 -0
  18. data/ext/markly/cmark_ctype.c +44 -0
  19. data/ext/markly/cmark_ctype.h +33 -0
  20. data/ext/markly/commonmark.c +519 -0
  21. data/ext/markly/config.h +76 -0
  22. data/ext/markly/core-extensions.c +27 -0
  23. data/ext/markly/entities.inc +2138 -0
  24. data/ext/markly/ext_scanners.c +1159 -0
  25. data/ext/markly/ext_scanners.h +24 -0
  26. data/ext/markly/extconf.rb +7 -0
  27. data/ext/markly/footnotes.c +40 -0
  28. data/ext/markly/footnotes.h +25 -0
  29. data/ext/markly/houdini.h +57 -0
  30. data/ext/markly/houdini_href_e.c +100 -0
  31. data/ext/markly/houdini_html_e.c +66 -0
  32. data/ext/markly/houdini_html_u.c +149 -0
  33. data/ext/markly/html.c +465 -0
  34. data/ext/markly/html.h +27 -0
  35. data/ext/markly/inlines.c +1633 -0
  36. data/ext/markly/inlines.h +29 -0
  37. data/ext/markly/iterator.c +159 -0
  38. data/ext/markly/iterator.h +26 -0
  39. data/ext/markly/latex.c +466 -0
  40. data/ext/markly/linked_list.c +37 -0
  41. data/ext/markly/man.c +278 -0
  42. data/ext/markly/map.c +122 -0
  43. data/ext/markly/map.h +41 -0
  44. data/ext/markly/markly.c +1226 -0
  45. data/ext/markly/markly.h +16 -0
  46. data/ext/markly/node.c +979 -0
  47. data/ext/markly/node.h +118 -0
  48. data/ext/markly/parser.h +58 -0
  49. data/ext/markly/plaintext.c +235 -0
  50. data/ext/markly/plugin.c +36 -0
  51. data/ext/markly/plugin.h +34 -0
  52. data/ext/markly/references.c +42 -0
  53. data/ext/markly/references.h +26 -0
  54. data/ext/markly/registry.c +63 -0
  55. data/ext/markly/registry.h +24 -0
  56. data/ext/markly/render.c +205 -0
  57. data/ext/markly/render.h +62 -0
  58. data/ext/markly/scanners.c +20382 -0
  59. data/ext/markly/scanners.h +62 -0
  60. data/ext/markly/scanners.re +326 -0
  61. data/ext/markly/strikethrough.c +167 -0
  62. data/ext/markly/strikethrough.h +9 -0
  63. data/ext/markly/syntax_extension.c +149 -0
  64. data/ext/markly/syntax_extension.h +34 -0
  65. data/ext/markly/table.c +803 -0
  66. data/ext/markly/table.h +12 -0
  67. data/ext/markly/tagfilter.c +60 -0
  68. data/ext/markly/tagfilter.h +8 -0
  69. data/ext/markly/tasklist.c +156 -0
  70. data/ext/markly/tasklist.h +8 -0
  71. data/ext/markly/utf8.c +317 -0
  72. data/ext/markly/utf8.h +35 -0
  73. data/ext/markly/xml.c +181 -0
  74. data/lib/markly.rb +43 -0
  75. data/lib/markly/flags.rb +37 -0
  76. data/lib/markly/markly.so +0 -0
  77. data/lib/markly/node.rb +70 -0
  78. data/lib/markly/node/inspect.rb +59 -0
  79. data/lib/markly/renderer.rb +133 -0
  80. data/lib/markly/renderer/html_renderer.rb +252 -0
  81. data/lib/markly/version.rb +5 -0
  82. metadata +211 -0
@@ -0,0 +1,35 @@
1
+ #ifndef CMARK_UTF8_H
2
+ #define CMARK_UTF8_H
3
+
4
+ #include <stdint.h>
5
+ #include "buffer.h"
6
+
7
+ #ifdef __cplusplus
8
+ extern "C" {
9
+ #endif
10
+
11
+ CMARK_GFM_EXPORT
12
+ void cmark_utf8proc_case_fold(cmark_strbuf *dest, const uint8_t *str,
13
+ bufsize_t len);
14
+
15
+ CMARK_GFM_EXPORT
16
+ void cmark_utf8proc_encode_char(int32_t uc, cmark_strbuf *buf);
17
+
18
+ CMARK_GFM_EXPORT
19
+ int cmark_utf8proc_iterate(const uint8_t *str, bufsize_t str_len, int32_t *dst);
20
+
21
+ CMARK_GFM_EXPORT
22
+ void cmark_utf8proc_check(cmark_strbuf *dest, const uint8_t *line,
23
+ bufsize_t size);
24
+
25
+ CMARK_GFM_EXPORT
26
+ int cmark_utf8proc_is_space(int32_t uc);
27
+
28
+ CMARK_GFM_EXPORT
29
+ int cmark_utf8proc_is_punctuation(int32_t uc);
30
+
31
+ #ifdef __cplusplus
32
+ }
33
+ #endif
34
+
35
+ #endif
@@ -0,0 +1,181 @@
1
+ #include <stdlib.h>
2
+ #include <stdio.h>
3
+ #include <string.h>
4
+ #include <assert.h>
5
+
6
+ #include "config.h"
7
+ #include "cmark-gfm.h"
8
+ #include "node.h"
9
+ #include "buffer.h"
10
+ #include "houdini.h"
11
+ #include "syntax_extension.h"
12
+
13
+ #define BUFFER_SIZE 100
14
+
15
+ // Functions to convert cmark_nodes to XML strings.
16
+
17
+ static void escape_xml(cmark_strbuf *dest, const unsigned char *source,
18
+ bufsize_t length) {
19
+ houdini_escape_html0(dest, source, length, 0);
20
+ }
21
+
22
+ struct render_state {
23
+ cmark_strbuf *xml;
24
+ int indent;
25
+ };
26
+
27
+ static CMARK_INLINE void indent(struct render_state *state) {
28
+ int i;
29
+ for (i = 0; i < state->indent; i++) {
30
+ cmark_strbuf_putc(state->xml, ' ');
31
+ }
32
+ }
33
+
34
+ static int S_render_node(cmark_node *node, cmark_event_type ev_type,
35
+ struct render_state *state, int options) {
36
+ cmark_strbuf *xml = state->xml;
37
+ bool literal = false;
38
+ cmark_delim_type delim;
39
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
40
+ char buffer[BUFFER_SIZE];
41
+
42
+ if (entering) {
43
+ indent(state);
44
+ cmark_strbuf_putc(xml, '<');
45
+ cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
46
+
47
+ if (options & CMARK_OPT_SOURCEPOS && node->start_line != 0) {
48
+ snprintf(buffer, BUFFER_SIZE, " sourcepos=\"%d:%d-%d:%d\"",
49
+ node->start_line, node->start_column, node->end_line,
50
+ node->end_column);
51
+ cmark_strbuf_puts(xml, buffer);
52
+ }
53
+
54
+ if (node->extension && node->extension->xml_attr_func) {
55
+ const char* r = node->extension->xml_attr_func(node->extension, node);
56
+ if (r != NULL)
57
+ cmark_strbuf_puts(xml, r);
58
+ }
59
+
60
+ literal = false;
61
+
62
+ switch (node->type) {
63
+ case CMARK_NODE_DOCUMENT:
64
+ cmark_strbuf_puts(xml, " xmlns=\"http://commonmark.org/xml/1.0\"");
65
+ break;
66
+ case CMARK_NODE_TEXT:
67
+ case CMARK_NODE_CODE:
68
+ case CMARK_NODE_HTML_BLOCK:
69
+ case CMARK_NODE_HTML_INLINE:
70
+ cmark_strbuf_puts(xml, " xml:space=\"preserve\">");
71
+ escape_xml(xml, node->as.literal.data, node->as.literal.len);
72
+ cmark_strbuf_puts(xml, "</");
73
+ cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
74
+ literal = true;
75
+ break;
76
+ case CMARK_NODE_LIST:
77
+ switch (cmark_node_get_list_type(node)) {
78
+ case CMARK_ORDERED_LIST:
79
+ cmark_strbuf_puts(xml, " type=\"ordered\"");
80
+ snprintf(buffer, BUFFER_SIZE, " start=\"%d\"",
81
+ cmark_node_get_list_start(node));
82
+ cmark_strbuf_puts(xml, buffer);
83
+ delim = cmark_node_get_list_delim(node);
84
+ if (delim == CMARK_PAREN_DELIM) {
85
+ cmark_strbuf_puts(xml, " delim=\"paren\"");
86
+ } else if (delim == CMARK_PERIOD_DELIM) {
87
+ cmark_strbuf_puts(xml, " delim=\"period\"");
88
+ }
89
+ break;
90
+ case CMARK_BULLET_LIST:
91
+ cmark_strbuf_puts(xml, " type=\"bullet\"");
92
+ break;
93
+ default:
94
+ break;
95
+ }
96
+ snprintf(buffer, BUFFER_SIZE, " tight=\"%s\"",
97
+ (cmark_node_get_list_tight(node) ? "true" : "false"));
98
+ cmark_strbuf_puts(xml, buffer);
99
+ break;
100
+ case CMARK_NODE_HEADING:
101
+ snprintf(buffer, BUFFER_SIZE, " level=\"%d\"", node->as.heading.level);
102
+ cmark_strbuf_puts(xml, buffer);
103
+ break;
104
+ case CMARK_NODE_CODE_BLOCK:
105
+ if (node->as.code.info.len > 0) {
106
+ cmark_strbuf_puts(xml, " info=\"");
107
+ escape_xml(xml, node->as.code.info.data, node->as.code.info.len);
108
+ cmark_strbuf_putc(xml, '"');
109
+ }
110
+ cmark_strbuf_puts(xml, " xml:space=\"preserve\">");
111
+ escape_xml(xml, node->as.code.literal.data, node->as.code.literal.len);
112
+ cmark_strbuf_puts(xml, "</");
113
+ cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
114
+ literal = true;
115
+ break;
116
+ case CMARK_NODE_CUSTOM_BLOCK:
117
+ case CMARK_NODE_CUSTOM_INLINE:
118
+ cmark_strbuf_puts(xml, " on_enter=\"");
119
+ escape_xml(xml, node->as.custom.on_enter.data,
120
+ node->as.custom.on_enter.len);
121
+ cmark_strbuf_putc(xml, '"');
122
+ cmark_strbuf_puts(xml, " on_exit=\"");
123
+ escape_xml(xml, node->as.custom.on_exit.data,
124
+ node->as.custom.on_exit.len);
125
+ cmark_strbuf_putc(xml, '"');
126
+ break;
127
+ case CMARK_NODE_LINK:
128
+ case CMARK_NODE_IMAGE:
129
+ cmark_strbuf_puts(xml, " destination=\"");
130
+ escape_xml(xml, node->as.link.url.data, node->as.link.url.len);
131
+ cmark_strbuf_putc(xml, '"');
132
+ cmark_strbuf_puts(xml, " title=\"");
133
+ escape_xml(xml, node->as.link.title.data, node->as.link.title.len);
134
+ cmark_strbuf_putc(xml, '"');
135
+ break;
136
+ default:
137
+ break;
138
+ }
139
+ if (node->first_child) {
140
+ state->indent += 2;
141
+ } else if (!literal) {
142
+ cmark_strbuf_puts(xml, " /");
143
+ }
144
+ cmark_strbuf_puts(xml, ">\n");
145
+
146
+ } else if (node->first_child) {
147
+ state->indent -= 2;
148
+ indent(state);
149
+ cmark_strbuf_puts(xml, "</");
150
+ cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
151
+ cmark_strbuf_puts(xml, ">\n");
152
+ }
153
+
154
+ return 1;
155
+ }
156
+
157
+ char *cmark_render_xml(cmark_node *root, int options) {
158
+ return cmark_render_xml_with_mem(root, options, cmark_node_mem(root));
159
+ }
160
+
161
+ char *cmark_render_xml_with_mem(cmark_node *root, int options, cmark_mem *mem) {
162
+ char *result;
163
+ cmark_strbuf xml = CMARK_BUF_INIT(mem);
164
+ cmark_event_type ev_type;
165
+ cmark_node *cur;
166
+ struct render_state state = {&xml, 0};
167
+
168
+ cmark_iter *iter = cmark_iter_new(root);
169
+
170
+ cmark_strbuf_puts(state.xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
171
+ cmark_strbuf_puts(state.xml,
172
+ "<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n");
173
+ while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
174
+ cur = cmark_iter_get_node(iter);
175
+ S_render_node(cur, ev_type, &state, options);
176
+ }
177
+ result = (char *)cmark_strbuf_detach(&xml);
178
+
179
+ cmark_iter_free(iter);
180
+ return result;
181
+ }
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # The compiled library.
5
+ require_relative 'markly/markly'
6
+
7
+ require_relative 'markly/flags'
8
+ require_relative 'markly/node'
9
+ require_relative 'markly/renderer'
10
+ require_relative 'markly/renderer/html_renderer'
11
+ require_relative 'markly/version'
12
+
13
+ module Markly
14
+ # Public: Parses a Markdown string into a `document` node.
15
+ #
16
+ # string - {String} to be parsed
17
+ # option - A {Symbol} or {Array of Symbol}s indicating the parse options
18
+ # extensions - An {Array of Symbol}s indicating the extensions to use
19
+ #
20
+ # Returns the `parser` node.
21
+ def self.parse(text, flags: DEFAULT, extensions: nil)
22
+ parser = Parser.new(flags)
23
+
24
+ extensions&.each do |extension|
25
+ parser.enable(extension)
26
+ end
27
+
28
+ return parser.parse(text.encode(Encoding::UTF_8))
29
+ end
30
+
31
+ # Public: Parses a Markdown string into an HTML string.
32
+ #
33
+ # text - A {String} of text
34
+ # option - Either a {Symbol} or {Array of Symbol}s indicating the render options
35
+ # extensions - An {Array of Symbol}s indicating the extensions to use
36
+ #
37
+ # Returns a {String} of converted HTML.
38
+ def self.render_html(text, flags: DEFAULT, parse_flags: flags, render_flags: flags, extensions: [])
39
+ root = self.parse(text, flags: parse_flags, extensions: extensions)
40
+
41
+ return root.to_html(flags: render_flags, extensions: extensions)
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Markly
4
+ DEFAULT = 0
5
+ VALIDATE_UTF8 = 1 << 9
6
+ SMART = 1 << 10
7
+ LIBERAL_HTML_TAG = 1 << 12
8
+ FOOTNOTES = 1 << 13
9
+ STRIKETHROUGH_DOUBLE_TILDE = 1 << 14
10
+ UNSAFE = 1 << 17
11
+
12
+ PARSE_FLAGS = {
13
+ validate_utf8: VALIDATE_UTF8,
14
+ smart_quotes: SMART,
15
+ liberal_html_tags: LIBERAL_HTML_TAG,
16
+ footnotes: FOOTNOTES,
17
+ strikethrough_double_tilde: STRIKETHROUGH_DOUBLE_TILDE,
18
+ unsafe: UNSAFE,
19
+ }
20
+
21
+ SOURCE_POSITION = 1 << 1
22
+ HARD_BREAKS = 1 << 2
23
+ NO_BREAKS = 1 << 4
24
+ GITHUB_PRE_LANG = 1 << 11
25
+ TABLE_PREFER_STYLE_ATTRIBUTES = 1 << 15
26
+ FULL_INFO_STRING = 1 << 16
27
+
28
+ RENDER_FLAGS = {
29
+ source_position: SOURCE_POSITION,
30
+ hard_breaks: HARD_BREAKS,
31
+ no_breaks: NO_BREAKS,
32
+ pre_lang: GITHUB_PRE_LANG,
33
+ table_prefer_style_attributes: TABLE_PREFER_STYLE_ATTRIBUTES,
34
+ full_info_string: FULL_INFO_STRING,
35
+ unsafe: UNSAFE,
36
+ }
37
+ end
Binary file
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'node/inspect'
4
+
5
+ module Markly
6
+ class Node
7
+ include Enumerable
8
+ include Inspect
9
+
10
+ # Public: An iterator that "walks the tree," descending into children recursively.
11
+ #
12
+ # blk - A {Proc} representing the action to take for each child
13
+ def walk(&block)
14
+ return enum_for(:walk) unless block_given?
15
+
16
+ yield self
17
+ each do |child|
18
+ child.walk(&block)
19
+ end
20
+ end
21
+
22
+ # Public: Convert the node to an HTML string.
23
+ #
24
+ # options - A {Symbol} or {Array of Symbol}s indicating the render options
25
+ # extensions - An {Array of Symbol}s indicating the extensions to use
26
+ #
27
+ # Returns a {String}.
28
+ def to_html(flags: DEFAULT, extensions: [])
29
+ _render_html(flags, extensions).force_encoding('utf-8')
30
+ end
31
+
32
+ # Public: Convert the node to a CommonMark string.
33
+ #
34
+ # options - A {Symbol} or {Array of Symbol}s indicating the render options
35
+ # width - Column to wrap the output at
36
+ #
37
+ # Returns a {String}.
38
+ def to_commonmark(flags: DEFAULT, width: 120)
39
+ _render_commonmark(flags, width).force_encoding('utf-8')
40
+ end
41
+
42
+ # Public: Convert the node to a plain text string.
43
+ #
44
+ # options - A {Symbol} or {Array of Symbol}s indicating the render options
45
+ # width - Column to wrap the output at
46
+ #
47
+ # Returns a {String}.
48
+ def to_plaintext(flags: DEFAULT, width: 120)
49
+ _render_plaintext(flags, width).force_encoding('utf-8')
50
+ end
51
+
52
+ # Public: Iterate over the children (if any) of the current pointer.
53
+ def each
54
+ return enum_for(:each) unless block_given?
55
+
56
+ child = first_child
57
+ while child
58
+ nextchild = child.next
59
+ yield child
60
+ child = nextchild
61
+ end
62
+ end
63
+
64
+ # Deprecated: Please use `each` instead
65
+ def each_child(&block)
66
+ warn '[DEPRECATION] `each_child` is deprecated. Please use `each` instead.'
67
+ each(&block)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pp'
4
+
5
+ module Markly
6
+ class Node
7
+ module Inspect
8
+ PP_INDENT_SIZE = 2
9
+
10
+ def inspect
11
+ PP.pp(self, +'', Float::INFINITY)
12
+ end
13
+
14
+ # @param printer [PrettyPrint] pp
15
+ def pretty_print(printer)
16
+ printer.group(PP_INDENT_SIZE, "#<#{self.class}(#{type}):", '>') do
17
+ printer.breakable
18
+
19
+ attrs = %i[
20
+ source_position
21
+ string_content
22
+ url
23
+ title
24
+ header_level
25
+ list_type
26
+ list_start
27
+ list_tight
28
+ fence_info
29
+ ].map do |name|
30
+ begin
31
+ [name, __send__(name)]
32
+ rescue Error
33
+ nil
34
+ end
35
+ end.compact
36
+
37
+ printer.seplist(attrs) do |name, value|
38
+ printer.text "#{name}="
39
+ printer.pp value
40
+ end
41
+
42
+ if first_child
43
+ printer.breakable
44
+ printer.group(PP_INDENT_SIZE) do
45
+ children = []
46
+ node = first_child
47
+ while node
48
+ children << node
49
+ node = node.next
50
+ end
51
+ printer.text 'children='
52
+ printer.pp children
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'stringio'
5
+
6
+ module Markly
7
+ class Renderer
8
+ attr_accessor :in_tight, :warnings, :in_plain
9
+ def initialize(flags: DEFAULT, extensions: [])
10
+ @flags = flags
11
+ @stream = StringIO.new(+'')
12
+ @need_blocksep = false
13
+ @warnings = Set.new
14
+ @in_tight = false
15
+ @in_plain = false
16
+ @tagfilter = extensions.include?(:tagfilter)
17
+ end
18
+
19
+ def out(*args)
20
+ args.each do |arg|
21
+ if arg == :children
22
+ @node.each { |child| out(child) }
23
+ elsif arg.is_a?(Array)
24
+ arg.each { |x| render(x) }
25
+ elsif arg.is_a?(Node)
26
+ render(arg)
27
+ else
28
+ @stream.write(arg)
29
+ end
30
+ end
31
+ end
32
+
33
+ def render(node)
34
+ @node = node
35
+ if node.type == :document
36
+ document(node)
37
+ @stream.string
38
+ elsif @in_plain && node.type != :text && node.type != :softbreak
39
+ node.each { |child| render(child) }
40
+ else
41
+ begin
42
+ send(node.type, node)
43
+ rescue NoMethodError => e
44
+ @warnings.add("WARNING: #{node.type} not implemented.")
45
+ raise e
46
+ end
47
+ end
48
+ end
49
+
50
+ def document(_node)
51
+ out(:children)
52
+ end
53
+
54
+ def code_block(node)
55
+ code_block(node)
56
+ end
57
+
58
+ def reference_def(_node); end
59
+
60
+ def cr
61
+ return if @stream.string.empty? || @stream.string[-1] == "\n"
62
+
63
+ out("\n")
64
+ end
65
+
66
+ def blocksep
67
+ out("\n")
68
+ end
69
+
70
+ def containersep
71
+ cr unless @in_tight
72
+ end
73
+
74
+ def block
75
+ cr
76
+ yield
77
+ cr
78
+ end
79
+
80
+ def container(starter, ender)
81
+ out(starter)
82
+ yield
83
+ out(ender)
84
+ end
85
+
86
+ def plain
87
+ old_in_plain = @in_plain
88
+ @in_plain = true
89
+ yield
90
+ @in_plain = old_in_plain
91
+ end
92
+
93
+ private
94
+
95
+ def escape_href(str)
96
+ @node.html_escape_href(str)
97
+ end
98
+
99
+ def escape_html(str)
100
+ @node.html_escape_html(str)
101
+ end
102
+
103
+ def tagfilter(str)
104
+ if @tagfilter
105
+ str.gsub(
106
+ %r{
107
+ <
108
+ (
109
+ title|textarea|style|xmp|iframe|
110
+ noembed|noframes|script|plaintext
111
+ )
112
+ (?=\s|>|/>)
113
+ }xi,
114
+ '&lt;\1'
115
+ )
116
+ else
117
+ str
118
+ end
119
+ end
120
+
121
+ def source_position(node)
122
+ return '' unless flag_enabled?(SOURCE_POSITION)
123
+
124
+ s = node.source_position
125
+ " data-sourcepos=\"#{s[:start_line]}:#{s[:start_column]}-" \
126
+ "#{s[:end_line]}:#{s[:end_column]}\""
127
+ end
128
+
129
+ def flag_enabled?(flag)
130
+ (@flags & flag) != 0
131
+ end
132
+ end
133
+ end