luoma 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +2 -0
  3. data/.rdoc_options +16 -0
  4. data/CHANGELOG.md +5 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +68 -0
  7. data/Rakefile +69 -0
  8. data/Steepfile +41 -0
  9. data/certs/jgrp.pem +27 -0
  10. data/docs/configuration.md +180 -0
  11. data/docs/custom_filters.md +3 -0
  12. data/docs/custom_tags.md +3 -0
  13. data/docs/expressions.md +3 -0
  14. data/docs/extension_types.md +41 -0
  15. data/docs/filter_reference.md +1702 -0
  16. data/docs/index.md +73 -0
  17. data/docs/luoma_for_template_authors.md +3 -0
  18. data/docs/markdown.md +111 -0
  19. data/docs/predicate_reference.md +3 -0
  20. data/docs/static_analysis.md +3 -0
  21. data/docs/tag_reference.md +352 -0
  22. data/docs/template_loaders.md +177 -0
  23. data/docs/undefined_variables.md +3 -0
  24. data/docs-requirements.txt +11 -0
  25. data/docs_/cycle.md +17 -0
  26. data/docs_/font_rendering_example.md +157 -0
  27. data/docs_/header_block_example.md +51 -0
  28. data/docs_/increment_and_decrement.md +49 -0
  29. data/docs_/logo_style_example.md +40 -0
  30. data/docs_/migration.md +11 -0
  31. data/docs_/notes.md +19 -0
  32. data/lib/luoma/cache.rb +80 -0
  33. data/lib/luoma/chain_hash.rb +54 -0
  34. data/lib/luoma/context.rb +227 -0
  35. data/lib/luoma/drop.rb +84 -0
  36. data/lib/luoma/drops/block.rb +33 -0
  37. data/lib/luoma/drops/expression.rb +29 -0
  38. data/lib/luoma/drops/html_safe.rb +36 -0
  39. data/lib/luoma/drops/range.rb +69 -0
  40. data/lib/luoma/drops/undefined.rb +119 -0
  41. data/lib/luoma/environment.rb +479 -0
  42. data/lib/luoma/errors.rb +79 -0
  43. data/lib/luoma/escape.rb +35 -0
  44. data/lib/luoma/expression.rb +1271 -0
  45. data/lib/luoma/filter.rb +97 -0
  46. data/lib/luoma/filters/array.rb +372 -0
  47. data/lib/luoma/filters/date.rb +19 -0
  48. data/lib/luoma/filters/default.rb +16 -0
  49. data/lib/luoma/filters/json.rb +14 -0
  50. data/lib/luoma/filters/math.rb +84 -0
  51. data/lib/luoma/filters/size.rb +13 -0
  52. data/lib/luoma/filters/slice.rb +52 -0
  53. data/lib/luoma/filters/sort.rb +113 -0
  54. data/lib/luoma/filters/string.rb +186 -0
  55. data/lib/luoma/fnv.rb +15 -0
  56. data/lib/luoma/lexer.rb +106 -0
  57. data/lib/luoma/lexer_legacy.rb +396 -0
  58. data/lib/luoma/lexer_unified.rb +279 -0
  59. data/lib/luoma/loader.rb +50 -0
  60. data/lib/luoma/loaders/choice_loader.rb +20 -0
  61. data/lib/luoma/loaders/file_system_loader.rb +75 -0
  62. data/lib/luoma/loaders/mixins.rb +52 -0
  63. data/lib/luoma/markup.rb +109 -0
  64. data/lib/luoma/parser.rb +252 -0
  65. data/lib/luoma/parser_unified.rb +979 -0
  66. data/lib/luoma/predicates/blank.rb +19 -0
  67. data/lib/luoma/predicates/defined.rb +10 -0
  68. data/lib/luoma/predicates/empty.rb +15 -0
  69. data/lib/luoma/predicates/type.rb +35 -0
  70. data/lib/luoma/static_analysis.rb +427 -0
  71. data/lib/luoma/tags/assign.rb +63 -0
  72. data/lib/luoma/tags/break.rb +23 -0
  73. data/lib/luoma/tags/capture.rb +43 -0
  74. data/lib/luoma/tags/case.rb +137 -0
  75. data/lib/luoma/tags/comment.rb +17 -0
  76. data/lib/luoma/tags/continue.rb +23 -0
  77. data/lib/luoma/tags/define.rb +42 -0
  78. data/lib/luoma/tags/else.rb +30 -0
  79. data/lib/luoma/tags/for.rb +108 -0
  80. data/lib/luoma/tags/if.rb +109 -0
  81. data/lib/luoma/tags/import.rb +91 -0
  82. data/lib/luoma/tags/include.rb +77 -0
  83. data/lib/luoma/tags/output.rb +20 -0
  84. data/lib/luoma/tags/raw.rb +26 -0
  85. data/lib/luoma/tags/render.rb +92 -0
  86. data/lib/luoma/tags/with.rb +55 -0
  87. data/lib/luoma/template.rb +178 -0
  88. data/lib/luoma/token.rb +84 -0
  89. data/lib/luoma/version.rb +5 -0
  90. data/lib/luoma.rb +75 -0
  91. data/sig/luoma/cache.rbs +44 -0
  92. data/sig/luoma/chain_hash.rbs +26 -0
  93. data/sig/luoma/context.rbs +115 -0
  94. data/sig/luoma/drop.rbs +44 -0
  95. data/sig/luoma/drops/block.rbs +10 -0
  96. data/sig/luoma/drops/expression.rbs +10 -0
  97. data/sig/luoma/drops/html_safe.rbs +14 -0
  98. data/sig/luoma/drops/range.rbs +15 -0
  99. data/sig/luoma/drops/undefined.rbs +27 -0
  100. data/sig/luoma/environment.rbs +212 -0
  101. data/sig/luoma/errors.rbs +67 -0
  102. data/sig/luoma/escape.rbs +15 -0
  103. data/sig/luoma/expression.rbs +514 -0
  104. data/sig/luoma/filter.rbs +66 -0
  105. data/sig/luoma/filters/array.rbs +74 -0
  106. data/sig/luoma/filters/date.rbs +9 -0
  107. data/sig/luoma/filters/default.rbs +8 -0
  108. data/sig/luoma/filters/json.rbs +7 -0
  109. data/sig/luoma/filters/math.rbs +37 -0
  110. data/sig/luoma/filters/size.rbs +6 -0
  111. data/sig/luoma/filters/slice.rbs +10 -0
  112. data/sig/luoma/filters/sort.rbs +17 -0
  113. data/sig/luoma/filters/string.rbs +103 -0
  114. data/sig/luoma/fnv.rbs +3 -0
  115. data/sig/luoma/lexer.rbs +42 -0
  116. data/sig/luoma/lexer_legacy.rbs +81 -0
  117. data/sig/luoma/lexer_unified.rbs +49 -0
  118. data/sig/luoma/loader.rbs +40 -0
  119. data/sig/luoma/loaders/choice_loader.rbs +9 -0
  120. data/sig/luoma/loaders/file_system_loader.rbs +19 -0
  121. data/sig/luoma/loaders/mixins.rbs +16 -0
  122. data/sig/luoma/markup.rbs +68 -0
  123. data/sig/luoma/parser.rbs +105 -0
  124. data/sig/luoma/parser_unified.rbs +150 -0
  125. data/sig/luoma/predicates/blank.rbs +6 -0
  126. data/sig/luoma/predicates/defined.rbs +6 -0
  127. data/sig/luoma/predicates/empty.rbs +6 -0
  128. data/sig/luoma/predicates/type.rbs +21 -0
  129. data/sig/luoma/static_analysis.rbs +141 -0
  130. data/sig/luoma/tags/assign.rbs +12 -0
  131. data/sig/luoma/tags/break.rbs +11 -0
  132. data/sig/luoma/tags/capture.rbs +14 -0
  133. data/sig/luoma/tags/case.rbs +36 -0
  134. data/sig/luoma/tags/comment.rbs +11 -0
  135. data/sig/luoma/tags/continue.rbs +11 -0
  136. data/sig/luoma/tags/define.rbs +16 -0
  137. data/sig/luoma/tags/else.rbs +17 -0
  138. data/sig/luoma/tags/for.rbs +26 -0
  139. data/sig/luoma/tags/if.rbs +45 -0
  140. data/sig/luoma/tags/import.rbs +13 -0
  141. data/sig/luoma/tags/include.rbs +13 -0
  142. data/sig/luoma/tags/output.rbs +13 -0
  143. data/sig/luoma/tags/raw.rbs +11 -0
  144. data/sig/luoma/tags/render.rbs +13 -0
  145. data/sig/luoma/tags/with.rbs +15 -0
  146. data/sig/luoma/template.rbs +117 -0
  147. data/sig/luoma/token.rbs +81 -0
  148. data/sig/luoma.rbs +11 -0
  149. data/zensical.toml +363 -0
  150. data.tar.gz.sig +0 -0
  151. metadata +233 -0
  152. metadata.gz.sig +0 -0
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Luoma
4
+ class RenderTag < Markup
5
+ #: (t_token, String, Parser) -> Markup
6
+ def self.parse(token, tag_name, parser)
7
+ name_expr = parser.parse_string_literal
8
+ name_value = name_expr.value
9
+
10
+ unless name_value
11
+ raise TemplateSyntaxError.new(
12
+ "expected a string literal",
13
+ name_expr.span,
14
+ parser.source,
15
+ parser.template_name
16
+ )
17
+ end
18
+
19
+ name = Name.new(name_expr.token, name_value)
20
+
21
+ # Leading commas are OK
22
+ parser.next if parser.kind == :token_comma
23
+
24
+ args = parser.parse_keyword_arguments(require_commas: true)
25
+ parser.carry_whitespace_control
26
+ parser.eat(:token_tag_end)
27
+ new(token, tag_name, name, args)
28
+ end
29
+
30
+ #: (t_token, String, Name, Array[KeywordArgument]) -> void
31
+ def initialize(token, tag_name, template_name, args)
32
+ super(token)
33
+ @tag_name = tag_name
34
+ @template_name = template_name
35
+ @args = args
36
+ end
37
+
38
+ #: (RenderContext, String) -> void
39
+ def render(context, buffer)
40
+ name = @template_name.value
41
+
42
+ begin
43
+ template = context.env.get_template(
44
+ name,
45
+ context: context,
46
+ tag: "render"
47
+ )
48
+ rescue TemplateNotFoundError => e
49
+ raise NoSuchTemplateError.new(
50
+ e.message,
51
+ @template_name.span,
52
+ context.template.source,
53
+ context.template.name
54
+ )
55
+ end
56
+
57
+ ctx = context.copy(
58
+ @args.to_h { |arg| [arg.name.value, arg.expression.evaluate(context)] },
59
+ block_scope: false,
60
+ template: template
61
+ )
62
+
63
+ template.render_with_context(ctx, buffer)
64
+ context.render_score_cumulative += ctx.render_score
65
+ end
66
+
67
+ #: () -> Array[Expression]
68
+ def expressions
69
+ @args.map(&:expression)
70
+ end
71
+
72
+ #: (RenderContext) -> Partial?
73
+ def partial(static_context)
74
+ name = @template_name.value
75
+
76
+ template = static_context.env.get_template(
77
+ name,
78
+ context: static_context,
79
+ tag: "render"
80
+ )
81
+
82
+ scope = @args.map(&:name)
83
+
84
+ Partial.new(
85
+ template,
86
+ :isolated,
87
+ scope,
88
+ Luoma.fnv1a32("#{name}-#{scope.map(&:value).join(":")}")
89
+ )
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Luoma
4
+ class WithTag < Markup
5
+ END_WITH_BLOCK = Set["endwith"]
6
+
7
+ #: (t_token, String, Parser) -> Markup
8
+ def self.parse(token, tag_name, parser)
9
+ args = parser.parse_keyword_arguments(require_commas: true)
10
+ parser.carry_whitespace_control
11
+ parser.eat(:token_tag_end)
12
+ block = parser.parse_block(stop: END_WITH_BLOCK)
13
+ parser.eat_empty_tag("endwith")
14
+ new(token, tag_name, args, block)
15
+ end
16
+
17
+ #: (t_token, String, Array[KeywordArgument], t_block) -> void
18
+ def initialize(token, tag_name, args, block)
19
+ super(token)
20
+ @tag_name = tag_name
21
+ @args = args
22
+ @block = block
23
+ @blank = Luoma.blank_block?(block)
24
+ end
25
+
26
+ #: (RenderContext, String) -> void
27
+ def render(context, buffer)
28
+ scope = {} #: t_namespace
29
+
30
+ context.extends(scope) do
31
+ # NOTE: Later arguments can use earlier arguments.
32
+ @args.each do |arg|
33
+ scope[arg.name.value] = arg.expression.evaluate(context)
34
+ end
35
+
36
+ Luoma.render_block(@block, context, buffer)
37
+ end
38
+ end
39
+
40
+ #: (RenderContext) -> Array[Markup]
41
+ def children(static_context)
42
+ @block.grep_v(String) #: Array[Markup]
43
+ end
44
+
45
+ #: () -> Array[Expression]
46
+ def expressions
47
+ @args.map(&:expression)
48
+ end
49
+
50
+ #: () -> Array[Name]
51
+ def block_scope
52
+ @args.map(&:name)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Luoma
4
+ class Template
5
+ attr_reader :env, :source, :nodes, :globals, :name, :overlay, :up_to_date
6
+
7
+ #: (Environment,
8
+ # String,
9
+ # t_block,
10
+ # ?globals: t_namespace?,
11
+ # ?name: String?,
12
+ # ?overlay: t_namespace?,
13
+ # ?up_to_date: Proc::_Callable?) -> void
14
+ def initialize(env, source, nodes, globals: nil, name: nil, overlay: nil, up_to_date: nil)
15
+ @env = env
16
+ @source = source
17
+ @nodes = nodes
18
+ @globals = globals
19
+ @name = name || ""
20
+ @overlay = overlay
21
+ @up_to_date = up_to_date
22
+ @lines = nil
23
+ end
24
+
25
+ # Render this template with template variables from `data`.
26
+ #: (t_namespace?) -> String
27
+ def render(data = nil)
28
+ buffer = +""
29
+ context = RenderContext.new(self, globals: make_globals(data))
30
+ render_with_context(context, buffer)
31
+ end
32
+
33
+ # Render this template to `buffer` with data from `context`.
34
+ # Returns buffer.
35
+ #
36
+ # _isolated_ templates raise errors for orphaned `{% break %}` and
37
+ # `{% continue %}` tags.
38
+ #
39
+ #: (RenderContext, String, ?isolated: bool) -> String
40
+ def render_with_context(context, buffer, isolated: true)
41
+ Luoma.render_block(@nodes, context, buffer, root: isolated)
42
+ buffer
43
+ end
44
+
45
+ # Return a copy of this template with different globals.
46
+ #: (t_namespace) -> Template
47
+ def with_globals(globals)
48
+ Template.new(
49
+ @env, @source, @nodes,
50
+ globals: globals,
51
+ name: @name,
52
+ overlay: @overlay,
53
+ up_to_date: @up_to_date
54
+ )
55
+ end
56
+
57
+ # Return this template's source code split into lines.
58
+ #
59
+ #: () -> Array[String]
60
+ def lines
61
+ @lines ||= @source.lines(chomp: false)
62
+ end
63
+
64
+ # Return `false` if this template is stale and needs to be loaded again.
65
+ # `nil` is returned if an `up_to_date` proc is not available.
66
+ def up_to_date?
67
+ @up_to_date&.call
68
+ end
69
+
70
+ # TODO: static analysis methods
71
+
72
+ # Statically analyze this template and report variable, tag and filter usage.
73
+ #
74
+ #: (?include_partials: bool) -> Luoma::StaticAnalysis::Result
75
+ def analyze(include_partials: false)
76
+ Luoma::StaticAnalysis.analyze(self, include_partials: include_partials)
77
+ end
78
+
79
+ # Return an array of variable names used in this template, without path segments.
80
+ #
81
+ #: (?include_partials: bool) -> Array[String]
82
+ def variables(include_partials: false)
83
+ analyze(include_partials: include_partials).variables.keys
84
+ end
85
+
86
+ # Return an array of variables used in this template, including path segments.
87
+ #
88
+ #: (?include_partials: bool) -> Array[String]
89
+ def variable_paths(include_partials: false)
90
+ analyze(include_partials: include_partials).variables.values.flatten.map { |v| v[:path] }.uniq
91
+ end
92
+
93
+ # Return an array of variables used in this template, each as an array of segments.
94
+ #
95
+ #: (?include_partials: bool) -> Array[Segments]
96
+ def variable_segments(include_partials: false)
97
+ analyze(include_partials: include_partials).variables.values.flatten.map { |v| v[:segments] }.uniq
98
+ end
99
+
100
+ # Return an array of global variables used in this template, without path segments.
101
+ #
102
+ #: (?include_partials: bool) -> Array[String]
103
+ def global_variables(include_partials: false)
104
+ analyze(include_partials: include_partials).globals.keys
105
+ end
106
+
107
+ # Return an array of global variables used in this template, including path segments.
108
+ #
109
+ #: (?include_partials: bool) -> Array[String]
110
+ def global_variable_paths(include_partials: false)
111
+ analyze(include_partials: include_partials).globals.values.flatten.map { |v| v[:path] }.uniq
112
+ end
113
+
114
+ # Return an array of global variables used in this template, each as an array of segments.
115
+ #
116
+ #: (?include_partials: bool) -> Array[Segments]
117
+ def global_variable_segments(include_partials: false)
118
+ analyze(include_partials: include_partials).globals.values.flatten.map { |v| v[:segments] }.uniq
119
+ end
120
+
121
+ # Return the names of all filters used in this template.
122
+ #
123
+ #: (?include_partials: bool) -> Array[String]
124
+ def filter_names(include_partials: false)
125
+ analyze(include_partials: include_partials).filters.keys
126
+ end
127
+
128
+ # Return the names of all tags used in this template.
129
+ #
130
+ #: (?include_partials: bool) -> Array[String]
131
+ def tag_names(include_partials: false)
132
+ analyze(include_partials: include_partials).tags.keys
133
+ end
134
+
135
+ # Return an array of comment nodes found in this template.
136
+ #
137
+ # Comment nodes have `token` and `text` attributes. Use `template.comments.map(&:text)`
138
+ # to get an array of comment strings. Each comment string includes leading and trailing
139
+ # whitespace.
140
+ #
141
+ # Note that this method does not try to load included or render templates when looking.
142
+ # for comment nodes.
143
+ #
144
+ #: () -> Array[Comment]
145
+ def comments
146
+ context = RenderContext.new(self)
147
+ nodes = [] # : Array[Comment]
148
+
149
+ # @type var visit: ^(Markup) -> void
150
+ visit = lambda do |node|
151
+ nodes << node if node.is_a?(Comment)
152
+
153
+ node.children(context).each do |child|
154
+ visit.call(child)
155
+ end
156
+ end
157
+
158
+ @nodes.each { |node| visit.call(node) unless node.is_a?(String) }
159
+
160
+ nodes
161
+ end
162
+
163
+ protected
164
+
165
+ # Return a new namespace including data from `namespace` and other
166
+ # namespaces pinned to this template.
167
+ #: (t_namespace?) -> t_namespace?
168
+ def make_globals(namespace)
169
+ return @globals if namespace.nil? && @overlay.nil?
170
+
171
+ namespace_ = {} #: t_namespace
172
+ namespace_.merge!(@globals) if @globals # steep:ignore
173
+ namespace_.merge!(@overlay) if @overlay # steep:ignore
174
+ namespace_.merge!(namespace) if namespace
175
+ namespace_
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Luoma
4
+ #: (t_token, String) -> String
5
+ def self.get_token_value(token, source)
6
+ source.byteslice(token[1]..token[2]) || raise
7
+ end
8
+
9
+ # Return a new token spanning `start` and `stop`.
10
+ #: (t_token, t_token) -> t_token
11
+ def self.span(start, stop)
12
+ [:token_span, start[1], stop[2]]
13
+ end
14
+
15
+ TOKEN_KIND_MAP = {
16
+ token_add: "ADD",
17
+ token_and: "AND",
18
+ token_arrow: "ARROW",
19
+ token_assign: "ASSIGN",
20
+ token_colon: "COLON",
21
+ token_comma: "COMMA",
22
+ token_comment: "COMMENT",
23
+ token_comment_end: "COMMENT_END",
24
+ token_comment_start: "COMMENT_START",
25
+ token_contains: "CONTAINS",
26
+ token_div: "DIV",
27
+ token_dot: "DOT",
28
+ token_double_dot: "DOUBLE_DOT",
29
+ token_double_escaped: "DOUBLE_ESCAPED",
30
+ token_double_quote: "DOUBLE_QUOTE",
31
+ token_double_quoted: "DOUBLE_QUOTED",
32
+ token_else: "ELSE",
33
+ token_eq: "EQ",
34
+ token_false: "FALSE",
35
+ token_float: "FLOAT",
36
+ token_ge: "GE",
37
+ token_gt: "GT",
38
+ token_ident: "IDENT",
39
+ token_if: "IF",
40
+ token_in: "IN",
41
+ token_int: "INT",
42
+ token_interpolation_end: "INTERPOLATION_END",
43
+ token_interpolation_start: "INTERPOLATION_START",
44
+ token_lbrace: "LBRACE",
45
+ token_lbracket: "LBRACKET",
46
+ token_le: "LE",
47
+ token_lparen: "LPAREN",
48
+ token_lt: "LT",
49
+ token_mod: "MOD",
50
+ token_mul: "MUL",
51
+ token_ne: "NE",
52
+ token_nil: "NIL",
53
+ token_not: "NOT",
54
+ token_null: "NULL",
55
+ token_or_else: "OR_ELSE",
56
+ token_or: "OR",
57
+ token_out_end: "OUT_END",
58
+ token_out_start: "OUT_START",
59
+ token_pipe: "PIPE",
60
+ token_question: "QUESTION",
61
+ token_rbrace: "RBRACE",
62
+ token_rbracket: "RBRACKET",
63
+ token_rparen: "RPAREN",
64
+ token_single_escaped: "SINGLE_ESCAPED",
65
+ token_single_quote: "SINGLE_QUOTE",
66
+ token_single_quoted: "SINGLE_QUOTED",
67
+ token_sub: "SUB",
68
+ token_tag_end: "TAG_END",
69
+ token_tag_name: "TAG_NAME",
70
+ token_tag_start: "TAG_START",
71
+ token_text: "TEXT",
72
+ token_triple_dot: "TRIPLE_DOT",
73
+ token_true: "TRUE",
74
+ token_wc: "WC",
75
+ token_eoi: "EOI",
76
+ token_unknown: "UNKNOWN",
77
+ token_hash: "HASH",
78
+ token_span: "SPAN",
79
+ token_blank: "BLANK",
80
+ token_empty: "EMPTY",
81
+ token_for: "FOR",
82
+ token_with: "WITH"
83
+ }.freeze #: Hash[t_token_kind, String]
84
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Luoma
4
+ VERSION = "0.1.0"
5
+ end
data/lib/luoma.rb ADDED
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal/util"
4
+ require "time"
5
+ require_relative "luoma/chain_hash"
6
+ require_relative "luoma/context"
7
+ require_relative "luoma/drop"
8
+ require_relative "luoma/environment"
9
+ require_relative "luoma/errors"
10
+ require_relative "luoma/expression"
11
+ require_relative "luoma/filter"
12
+ require_relative "luoma/fnv"
13
+ require_relative "luoma/lexer"
14
+ require_relative "luoma/lexer_unified"
15
+ require_relative "luoma/loader"
16
+ require_relative "luoma/loaders/choice_loader"
17
+ require_relative "luoma/loaders/mixins"
18
+ require_relative "luoma/loaders/file_system_loader"
19
+ require_relative "luoma/markup"
20
+ require_relative "luoma/parser"
21
+ require_relative "luoma/parser_unified"
22
+ require_relative "luoma/template"
23
+ require_relative "luoma/token"
24
+ require_relative "luoma/version"
25
+ require_relative "luoma/drops/block"
26
+ require_relative "luoma/drops/expression"
27
+ require_relative "luoma/drops/range"
28
+ require_relative "luoma/drops/undefined"
29
+ require_relative "luoma/filters/array"
30
+ require_relative "luoma/filters/date"
31
+ require_relative "luoma/filters/default"
32
+ require_relative "luoma/filters/json"
33
+ require_relative "luoma/filters/math"
34
+ require_relative "luoma/filters/size"
35
+ require_relative "luoma/filters/slice"
36
+ require_relative "luoma/filters/sort"
37
+ require_relative "luoma/filters/string"
38
+ require_relative "luoma/predicates/type"
39
+ require_relative "luoma/predicates/blank"
40
+ require_relative "luoma/predicates/empty"
41
+ require_relative "luoma/predicates/defined"
42
+ require_relative "luoma/static_analysis"
43
+ require_relative "luoma/tags/assign"
44
+ require_relative "luoma/tags/break"
45
+ require_relative "luoma/tags/capture"
46
+ require_relative "luoma/tags/case"
47
+ require_relative "luoma/tags/comment"
48
+ require_relative "luoma/tags/continue"
49
+ require_relative "luoma/tags/define"
50
+ require_relative "luoma/tags/else"
51
+ require_relative "luoma/tags/for"
52
+ require_relative "luoma/tags/if"
53
+ require_relative "luoma/tags/import"
54
+ require_relative "luoma/tags/include"
55
+ require_relative "luoma/tags/output"
56
+ require_relative "luoma/tags/raw"
57
+ require_relative "luoma/tags/render"
58
+ require_relative "luoma/tags/with"
59
+
60
+ module Luoma
61
+ DEFAULT_ENVIRONMENT = Environment.new
62
+
63
+ # Parse _source_ text as a template using the default template environment.
64
+ #: (String, ?globals: Hash[String, untyped]?) -> Template
65
+ def self.parse(source, globals: nil)
66
+ DEFAULT_ENVIRONMENT.parse(source, globals: globals)
67
+ end
68
+
69
+ # Parse and render template _source_ with _data_ as template variables and
70
+ # the default template environment.
71
+ #: (String, ?Hash[String, untyped]?) -> String
72
+ def self.render(source, data = nil)
73
+ DEFAULT_ENVIRONMENT.render(source, data)
74
+ end
75
+ end
@@ -0,0 +1,44 @@
1
+ module Luoma
2
+ # A least recently used cache relying on Ruby hash insertion order.
3
+ class LRUCache
4
+ @data: Hash[untyped, untyped]
5
+
6
+ @max_size: Integer
7
+
8
+ attr_reader max_size: Integer
9
+
10
+ def initialize: (?::Integer max_size) -> void
11
+
12
+ # Return the cached value or nil if _key_ does not exist.
13
+ def []: (untyped key) -> (nil | untyped)
14
+
15
+ def []=: (untyped key, untyped value) -> untyped
16
+
17
+ def length: () -> Integer
18
+
19
+ def keys: () -> Array[untyped]
20
+ end
21
+
22
+ # A thread safe least recently used cache.
23
+ class ThreadSafeLRUCache < LRUCache
24
+ include MonitorMixin
25
+
26
+ alias unsafe_get []
27
+
28
+ alias unsafe_set []=
29
+
30
+ alias unsafe_length length
31
+
32
+ alias unsafe_keys keys
33
+
34
+ def initialize: (?::Integer max_size) -> void
35
+
36
+ def []: (untyped key) -> untyped
37
+
38
+ def []=: (untyped key, untyped value) -> untyped
39
+
40
+ def length: () -> Integer
41
+
42
+ def keys: () -> Array[Integer]
43
+ end
44
+ end
@@ -0,0 +1,26 @@
1
+ module Luoma
2
+ # Combine multiple hashes for sequential lookup.
3
+ class ChainHash
4
+ @hashes: Array[Luoma::_Namespace]
5
+
6
+ # (*_Namespace) -> void
7
+ def initialize: (*_Namespace hashes) -> void
8
+
9
+ # (String) -> untyped
10
+ def []: (String key) -> untyped
11
+
12
+ # (String) -> bool
13
+ def key?: (String key) -> untyped
14
+
15
+ # (String, untyped) -> untyped
16
+ def fetch: (String key, ?untyped default) -> untyped
17
+
18
+ def size: () -> Integer
19
+
20
+ def push: (_Namespace hash) -> void
21
+
22
+ alias << push
23
+
24
+ def pop: () -> _Namespace?
25
+ end
26
+ end
@@ -0,0 +1,115 @@
1
+ module Luoma
2
+ class RenderContext
3
+ # The template being rendered.
4
+ @template: Template
5
+
6
+ # The Luoma environment this render context and associated template is
7
+ # bound to.
8
+ @env: Environment
9
+
10
+ # Developer-defined template variables passed down from the environment
11
+ # and template.
12
+ @globals: _Namespace
13
+
14
+ # The namespace for variables defined with `{% assign %}` and
15
+ # `{% capture %}`.
16
+ @locals: Hash[String, untyped]
17
+
18
+ # A namespace for `{% increment %}` and `{% decrement %}`.
19
+ @counters: Hash[String, untyped]
20
+
21
+ # The current template scope including `locals`, `globals` and `counters`.
22
+ # New block-scoped namespaces get pushed onto and popped off this chain
23
+ # map.
24
+ #
25
+ # Scopes are searched from right to left. New scopes are push on the
26
+ # right.
27
+ @scopes: ChainHash
28
+
29
+ # Names of tags that are disallowed in this context.
30
+ @disabled_tags: Set[String]?
31
+
32
+ # The number of times this render context has been extended or copied.
33
+ @context_depth: Integer
34
+
35
+ # A non-specific indicator of template local scope usage.
36
+ @assign_score: Integer
37
+
38
+ # A non-specific indicator of template local scope usage for the current
39
+ # template and all partial templates combined.
40
+ @assign_score_cumulative: Integer
41
+
42
+ # The number of nodes rendered for the current template.
43
+ @render_score: Integer
44
+
45
+ # The number of nodes rendered for the current template and all partial
46
+ # templates.
47
+ @render_score_cumulative: Integer
48
+
49
+ # A stack of interrupt signals used by `{% break %}` and
50
+ # `{% continue %}`, for example.
51
+ @interrupts: Array[Symbol]
52
+
53
+ # Registers supporting stateful tags. It's OK to use this map for storing
54
+ # custom tag state.
55
+ @registers: Hash[Symbol, untyped]
56
+
57
+ attr_reader env: Environment
58
+
59
+ attr_reader template: Template
60
+
61
+ attr_reader scopes: ChainHash
62
+
63
+ attr_reader disabled_tags: Set[String]?
64
+
65
+ attr_reader context_depth: Integer
66
+
67
+ attr_reader assign_score: Integer
68
+
69
+ attr_reader assign_score_cumulative: Integer
70
+
71
+ attr_accessor render_score: Integer
72
+
73
+ attr_accessor render_score_cumulative: Integer
74
+
75
+ attr_accessor interrupts: Array[Symbol]
76
+
77
+ attr_reader registers: Hash[Symbol, untyped]
78
+
79
+ attr_reader globals: _Namespace
80
+
81
+ attr_reader locals: Hash[String, untyped]
82
+
83
+ # (Template, ?globals: _Namespace?) -> void
84
+ def initialize: (Template template, ?globals: _Namespace?, ?disabled_tags: Set[String]?, ?context_depth: Integer?, ?assign_score_carry: Integer?, ?render_score_carry: Integer?) -> void
85
+
86
+ # (String) -> untyped
87
+ def resolve: (String name) -> untyped
88
+
89
+ # (untyped, Array[untyped]) -> untyped
90
+ def resolve_path: (untyped obj, untyped segments) -> untyped
91
+
92
+ # (String, untyped) -> void
93
+ def assign: (String name, untyped value) -> void
94
+
95
+ # (t_namespace, bool?, Set[String]?, Template?) -> RenderContext
96
+ def copy: (t_namespace namespace, ?block_scope: bool?, ?disabled_tags: Set[String]?, ?template: Template?) -> RenderContext
97
+
98
+ # (t_namespace, ?template: Template?) { () -> untyped } -> void
99
+ def extends: (t_namespace namespace, ?template: Template?) { () -> untyped } -> void
100
+
101
+ private
102
+
103
+ # (untyped) -> Integer
104
+ def assign_score_of: (untyped value) -> Integer
105
+
106
+ #: () -> void
107
+ def raise_for_context_depth: () -> void
108
+
109
+ # (Array[untyped], untyped) -> untyped
110
+ def resolve_array_segment: (Array[untyped] obj, untyped segment) -> untyped
111
+
112
+ # (Hash[untyped, untyped], untyped) -> untyped
113
+ def resolve_hash_segment: (Hash[untyped, untyped] obj, untyped segment) -> untyped
114
+ end
115
+ end