poml 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/lib/poml/components/base.rb +45 -7
- data/lib/poml/components/data.rb +259 -0
- data/lib/poml/components/examples.rb +159 -13
- data/lib/poml/components/formatting.rb +148 -0
- data/lib/poml/components/media.rb +34 -0
- data/lib/poml/components/meta.rb +248 -0
- data/lib/poml/components/template.rb +334 -0
- data/lib/poml/components/utilities.rb +508 -0
- data/lib/poml/components.rb +91 -2
- data/lib/poml/context.rb +41 -2
- data/lib/poml/parser.rb +128 -15
- data/lib/poml/renderer.rb +26 -7
- data/lib/poml/template_engine.rb +101 -4
- data/lib/poml/version.rb +1 -1
- data/lib/poml.rb +67 -1
- data/{README.md → readme.md} +9 -1
- metadata +8 -4
- data/examples/_generate_expects.py +0 -35
data/lib/poml/parser.rb
CHANGED
@@ -32,6 +32,9 @@ module Poml
|
|
32
32
|
# Handle escape characters
|
33
33
|
content = unescape_poml(content)
|
34
34
|
|
35
|
+
# Pre-process to handle JSON in attributes (convert \" to " inside attribute values)
|
36
|
+
content = preprocess_json_attributes(content)
|
37
|
+
|
35
38
|
# Remove XML comments but preserve surrounding whitespace
|
36
39
|
content = content.gsub(/(\s*)<!--.*?-->(\s*)/m) do |match|
|
37
40
|
before_space = $1
|
@@ -44,6 +47,9 @@ module Poml
|
|
44
47
|
end
|
45
48
|
end
|
46
49
|
|
50
|
+
# Convert HTML-style void elements to XML self-closing format
|
51
|
+
content = preprocess_void_elements(content)
|
52
|
+
|
47
53
|
# Apply template substitutions
|
48
54
|
content = @template_engine.substitute(content)
|
49
55
|
|
@@ -86,17 +92,15 @@ module Poml
|
|
86
92
|
text_content = child.to_s
|
87
93
|
next if text_content.strip.empty? # Only skip if completely empty when stripped
|
88
94
|
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
# Normalize the text: strip leading/trailing whitespace
|
94
|
-
normalized = text_content.strip
|
95
|
+
# For inline content, preserve spaces but normalize newlines
|
96
|
+
# Convert newlines to single spaces to avoid formatting issues
|
97
|
+
normalized = text_content.gsub(/\s*\n\s*/, ' ')
|
95
98
|
|
96
|
-
#
|
97
|
-
|
99
|
+
# Trim only if the content is just whitespace, otherwise preserve leading/trailing spaces
|
100
|
+
if normalized.strip.empty?
|
101
|
+
next
|
102
|
+
end
|
98
103
|
|
99
|
-
next if normalized.empty?
|
100
104
|
elements << Element.new(tag_name: :text, content: normalized)
|
101
105
|
when REXML::Element
|
102
106
|
# Convert REXML attributes to string hash
|
@@ -105,12 +109,27 @@ module Poml
|
|
105
109
|
attrs[name.downcase] = value.to_s
|
106
110
|
end
|
107
111
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
)
|
112
|
+
# Check for conditional and loop attributes before creating element
|
113
|
+
if_condition = attrs['if']
|
114
|
+
for_attribute = attrs['for']
|
115
|
+
|
116
|
+
# Handle conditional rendering
|
117
|
+
if if_condition && !evaluate_if_condition(if_condition)
|
118
|
+
next # Skip this element
|
119
|
+
end
|
120
|
+
|
121
|
+
# Handle for loop rendering
|
122
|
+
if for_attribute
|
123
|
+
loop_elements = render_for_loop(child, attrs, for_attribute)
|
124
|
+
elements.concat(loop_elements)
|
125
|
+
else
|
126
|
+
elements << Element.new(
|
127
|
+
tag_name: child.name.downcase.to_sym,
|
128
|
+
attributes: attrs,
|
129
|
+
content: extract_text_content(child),
|
130
|
+
children: parse_element(child)
|
131
|
+
)
|
132
|
+
end
|
114
133
|
end
|
115
134
|
end
|
116
135
|
|
@@ -149,5 +168,99 @@ module Poml
|
|
149
168
|
end
|
150
169
|
end
|
151
170
|
end
|
171
|
+
|
172
|
+
def evaluate_if_condition(condition)
|
173
|
+
value = @template_engine.evaluate_attribute_expression(condition)
|
174
|
+
!!value
|
175
|
+
end
|
176
|
+
|
177
|
+
def render_for_loop(xml_element, attrs, for_attribute)
|
178
|
+
# Parse for attribute like "i in [1,2,3]" or "item in items"
|
179
|
+
if for_attribute =~ /^(\w+)\s+in\s+(.+)$/
|
180
|
+
loop_var = $1
|
181
|
+
list_expr = $2.strip
|
182
|
+
|
183
|
+
# Evaluate the list expression
|
184
|
+
list = @template_engine.evaluate_attribute_expression(list_expr)
|
185
|
+
return [] unless list.is_a?(Array)
|
186
|
+
|
187
|
+
# Create elements for each item in the list
|
188
|
+
elements = []
|
189
|
+
list.each_with_index do |item, index|
|
190
|
+
# Create loop context
|
191
|
+
old_loop_var = @context.variables[loop_var]
|
192
|
+
old_loop_context = @context.variables['loop']
|
193
|
+
|
194
|
+
@context.variables[loop_var] = item
|
195
|
+
@context.variables['loop'] = {
|
196
|
+
'index' => index,
|
197
|
+
'length' => list.length,
|
198
|
+
'first' => index == 0,
|
199
|
+
'last' => index == list.length - 1
|
200
|
+
}
|
201
|
+
|
202
|
+
# Remove for attribute and process element normally
|
203
|
+
loop_attrs = attrs.dup
|
204
|
+
loop_attrs.delete('for')
|
205
|
+
|
206
|
+
element = Element.new(
|
207
|
+
tag_name: xml_element.name.downcase.to_sym,
|
208
|
+
attributes: loop_attrs,
|
209
|
+
content: extract_text_content(xml_element),
|
210
|
+
children: parse_element(xml_element)
|
211
|
+
)
|
212
|
+
|
213
|
+
elements << element
|
214
|
+
|
215
|
+
# Restore previous context
|
216
|
+
if old_loop_var
|
217
|
+
@context.variables[loop_var] = old_loop_var
|
218
|
+
else
|
219
|
+
@context.variables.delete(loop_var)
|
220
|
+
end
|
221
|
+
|
222
|
+
if old_loop_context
|
223
|
+
@context.variables['loop'] = old_loop_context
|
224
|
+
else
|
225
|
+
@context.variables.delete('loop')
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
elements
|
230
|
+
else
|
231
|
+
# Invalid for syntax, return empty
|
232
|
+
[]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def preprocess_void_elements(content)
|
237
|
+
# List of HTML void elements that should be self-closing in XML
|
238
|
+
void_elements = %w[br hr img input area base col embed link meta param source track wbr]
|
239
|
+
|
240
|
+
# Convert <element> to <element/> for void elements, but only if not already self-closing
|
241
|
+
void_elements.each do |element|
|
242
|
+
# Match opening tag that's not already self-closing
|
243
|
+
# Use a more specific pattern to avoid matching already self-closing tags
|
244
|
+
pattern = /<(#{element})(\s+[^>\/]*?)?(?<!\/)>/i
|
245
|
+
content = content.gsub(pattern) do |match|
|
246
|
+
element_name = $1
|
247
|
+
attributes = $2 || ''
|
248
|
+
"<#{element_name}#{attributes}/>"
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
content
|
253
|
+
end
|
254
|
+
|
255
|
+
def preprocess_json_attributes(content)
|
256
|
+
# Convert problematic characters in attribute values to make them valid XML
|
257
|
+
content = content.gsub(/\\"/, '"') # Handle JSON quotes
|
258
|
+
|
259
|
+
# Handle comparison operators in attribute values
|
260
|
+
content = content.gsub(/(\w+\s*=\s*"[^"]*?)(<)([^"]*?")/m, '\1<\3')
|
261
|
+
content = content.gsub(/(\w+\s*=\s*"[^"]*?)(>)([^"]*?")/m, '\1>\3')
|
262
|
+
|
263
|
+
content
|
264
|
+
end
|
152
265
|
end
|
153
266
|
end
|
data/lib/poml/renderer.rb
CHANGED
@@ -42,19 +42,38 @@ module Poml
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def render_dict(elements)
|
45
|
+
# Render content first to allow meta components to modify context
|
46
|
+
content = render_raw(elements)
|
47
|
+
|
48
|
+
# Gather metadata after rendering
|
49
|
+
metadata = {
|
50
|
+
'chat' => @context.chat,
|
51
|
+
'stylesheet' => @context.stylesheet,
|
52
|
+
'variables' => @context.variables
|
53
|
+
}
|
54
|
+
|
55
|
+
# Include custom metadata (title, description, etc.)
|
56
|
+
metadata.merge!(@context.custom_metadata) if @context.custom_metadata && !@context.custom_metadata.empty?
|
57
|
+
|
58
|
+
# Include additional metadata if present
|
59
|
+
metadata['response_schema'] = @context.response_schema if @context.response_schema
|
60
|
+
metadata['tools'] = @context.tools if @context.tools && !@context.tools.empty?
|
61
|
+
metadata['runtime_parameters'] = @context.runtime_parameters if @context.runtime_parameters && !@context.runtime_parameters.empty?
|
62
|
+
|
45
63
|
{
|
46
|
-
'content' =>
|
47
|
-
'metadata' =>
|
48
|
-
'chat' => @context.chat,
|
49
|
-
'stylesheet' => @context.stylesheet,
|
50
|
-
'variables' => @context.variables
|
51
|
-
}
|
64
|
+
'content' => content,
|
65
|
+
'metadata' => metadata
|
52
66
|
}
|
53
67
|
end
|
54
68
|
|
55
69
|
def render_openai_chat(elements)
|
70
|
+
# First render to collect structured messages
|
56
71
|
content = render_raw(elements)
|
57
|
-
|
72
|
+
|
73
|
+
# Use structured messages if available
|
74
|
+
if @context.respond_to?(:chat_messages) && !@context.chat_messages.empty?
|
75
|
+
@context.chat_messages
|
76
|
+
elsif @context.chat
|
58
77
|
parse_chat_messages(content)
|
59
78
|
else
|
60
79
|
[{ 'role' => 'user', 'content' => content }]
|
data/lib/poml/template_engine.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Poml
|
2
|
-
# Template engine for handling {{variable}} substitutions
|
2
|
+
# Template engine for handling {{variable}} substitutions and control structures
|
3
3
|
class TemplateEngine
|
4
4
|
def initialize(context)
|
5
5
|
@context = context
|
@@ -15,6 +15,37 @@ module Poml
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def evaluate_attribute_expression(expression)
|
19
|
+
# Handle attribute expressions that might return non-string values
|
20
|
+
if expression =~ /^(\w+(?:\.\w+)*)\s*\+\s*(\d+)$/
|
21
|
+
variable_path = $1
|
22
|
+
increment = $2.to_i
|
23
|
+
|
24
|
+
value = get_nested_variable(variable_path)
|
25
|
+
if value.is_a?(Numeric)
|
26
|
+
value + increment
|
27
|
+
else
|
28
|
+
expression
|
29
|
+
end
|
30
|
+
elsif expression =~ /^(\w+(?:\.\w+)*)$/
|
31
|
+
# Simple variable or dot notation lookup
|
32
|
+
variable_path = $1
|
33
|
+
get_nested_variable(variable_path)
|
34
|
+
elsif expression =~ /^(true|false)$/i
|
35
|
+
$1.downcase == 'true'
|
36
|
+
elsif expression =~ /^-?\d+$/
|
37
|
+
$1.to_i
|
38
|
+
elsif expression =~ /^-?\d*\.\d+$/
|
39
|
+
$1.to_f
|
40
|
+
elsif @context.variables.key?(expression)
|
41
|
+
# Direct variable lookup (backward compatibility)
|
42
|
+
@context.variables[expression]
|
43
|
+
else
|
44
|
+
# Try to evaluate as a more complex expression
|
45
|
+
evaluate_complex_expression(expression)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
18
49
|
private
|
19
50
|
|
20
51
|
def evaluate_expression(expression)
|
@@ -40,15 +71,81 @@ module Poml
|
|
40
71
|
# Direct variable lookup (backward compatibility)
|
41
72
|
@context.variables[expression].to_s
|
42
73
|
else
|
43
|
-
#
|
44
|
-
|
74
|
+
# Try to evaluate as a more complex expression
|
75
|
+
result = evaluate_complex_expression(expression)
|
76
|
+
result ? result.to_s : "{{#{expression}}}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def evaluate_complex_expression(expression)
|
81
|
+
# Handle more complex expressions like array literals, object access, etc.
|
82
|
+
|
83
|
+
# Try to parse as JSON first (for arrays and objects)
|
84
|
+
begin
|
85
|
+
require 'json'
|
86
|
+
return JSON.parse(expression)
|
87
|
+
rescue JSON::ParserError
|
88
|
+
# Not valid JSON, continue with other parsing
|
89
|
+
end
|
90
|
+
|
91
|
+
# Array literals like ['apple', 'banana', 'cherry']
|
92
|
+
if expression =~ /^\[(.+)\]$/
|
93
|
+
array_content = $1
|
94
|
+
# Simple parsing for string arrays
|
95
|
+
if array_content.match(/^'[^']*'(?:\s*,\s*'[^']*')*$/)
|
96
|
+
return array_content.split(',').map { |item| item.strip.gsub(/^'|'$/, '') }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Object literals like { name: 'John', age: 30 }
|
101
|
+
if expression =~ /^\{(.+)\}$/
|
102
|
+
# This would need a proper expression parser for full support
|
103
|
+
# For now, return the expression as-is
|
104
|
+
return expression
|
105
|
+
end
|
106
|
+
|
107
|
+
# Ternary operator like condition ? valueIfTrue : valueIfFalse
|
108
|
+
if expression =~ /^(.+)\s*\?\s*(.+)\s*:\s*(.+)$/
|
109
|
+
condition = $1.strip
|
110
|
+
true_value = $2.strip
|
111
|
+
false_value = $3.strip
|
112
|
+
|
113
|
+
condition_result = evaluate_condition(condition)
|
114
|
+
if condition_result
|
115
|
+
evaluate_expression(true_value)
|
116
|
+
else
|
117
|
+
evaluate_expression(false_value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
def evaluate_condition(condition)
|
125
|
+
# Simple condition evaluation
|
126
|
+
case condition
|
127
|
+
when 'true'
|
128
|
+
true
|
129
|
+
when 'false'
|
130
|
+
false
|
131
|
+
when /^!(.+)$/
|
132
|
+
!evaluate_condition($1.strip)
|
133
|
+
else
|
134
|
+
value = get_nested_variable(condition)
|
135
|
+
!!value
|
45
136
|
end
|
46
137
|
end
|
47
138
|
|
48
139
|
def get_nested_variable(path)
|
49
140
|
# Handle dot notation like "loop.index"
|
50
141
|
parts = path.split('.')
|
51
|
-
|
142
|
+
|
143
|
+
# Handle both Context objects and raw variable hashes
|
144
|
+
value = if @context.respond_to?(:variables)
|
145
|
+
@context.variables
|
146
|
+
else
|
147
|
+
@context
|
148
|
+
end
|
52
149
|
|
53
150
|
parts.each do |part|
|
54
151
|
if value.is_a?(Hash) && value.key?(part)
|
data/lib/poml/version.rb
CHANGED
data/lib/poml.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
require_relative "poml/version"
|
4
4
|
require_relative 'poml/context'
|
5
|
+
require_relative 'poml/template_engine'
|
5
6
|
require_relative 'poml/parser'
|
6
7
|
require_relative 'poml/renderer'
|
7
8
|
require_relative 'poml/components'
|
8
|
-
require_relative 'poml/template_engine'
|
9
9
|
|
10
10
|
module Poml
|
11
11
|
class Error < StandardError; end
|
@@ -50,4 +50,70 @@ module Poml
|
|
50
50
|
|
51
51
|
result
|
52
52
|
end
|
53
|
+
|
54
|
+
def self.parse(content, context: nil)
|
55
|
+
context ||= Context.new
|
56
|
+
parser = Parser.new(context)
|
57
|
+
parser.parse(content)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.render(content, format: 'text', context: nil, **options)
|
61
|
+
context ||= Context.new(**options)
|
62
|
+
elements = parse(content, context: context)
|
63
|
+
renderer = Renderer.new(context)
|
64
|
+
renderer.render(elements, format)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Convenience method for quick text rendering
|
68
|
+
def self.to_text(content, **options)
|
69
|
+
render(content, format: 'raw', **options)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Convenience method for chat format
|
73
|
+
def self.to_chat(content, **options)
|
74
|
+
render(content, format: 'openai_chat', **options)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Convenience method for dict format
|
78
|
+
def self.to_dict(content, **options)
|
79
|
+
render(content, format: 'dict', **options)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Legacy method for backward compatibility
|
83
|
+
def self.process(markup:, format: 'dict', **options)
|
84
|
+
# Handle file paths
|
85
|
+
content = if File.exist?(markup)
|
86
|
+
File.read(markup)
|
87
|
+
else
|
88
|
+
markup
|
89
|
+
end
|
90
|
+
|
91
|
+
# Extract output file option
|
92
|
+
output_file = options.delete(:output_file)
|
93
|
+
|
94
|
+
# Extract context from various parameter formats
|
95
|
+
if options.key?(:context)
|
96
|
+
# If context is provided explicitly, use it as variables
|
97
|
+
context_options = {}
|
98
|
+
context_options[:variables] = options.delete(:context)
|
99
|
+
else
|
100
|
+
# Extract known context options and ignore unknown ones
|
101
|
+
context_options = {}
|
102
|
+
context_options[:variables] = options.delete(:variables) || options.reject { |k, v| [:stylesheet, :chat, :syntax].include?(k) }
|
103
|
+
end
|
104
|
+
|
105
|
+
context_options[:stylesheet] = options.delete(:stylesheet) if options.key?(:stylesheet)
|
106
|
+
context_options[:chat] = options.delete(:chat) if options.key?(:chat)
|
107
|
+
context_options[:syntax] = options.delete(:syntax) if options.key?(:syntax)
|
108
|
+
|
109
|
+
result = render(content, format: format, **context_options)
|
110
|
+
|
111
|
+
# Write to file if output_file is specified
|
112
|
+
if output_file
|
113
|
+
File.write(output_file, result)
|
114
|
+
'' # Return empty string when writing to file
|
115
|
+
else
|
116
|
+
result
|
117
|
+
end
|
118
|
+
end
|
53
119
|
end
|
data/{README.md → readme.md}
RENAMED
@@ -6,17 +6,25 @@ A Ruby implementation of the POML (Prompt Oriented Markup Language) interpreter.
|
|
6
6
|
|
7
7
|
This is a **Ruby port** of the original [POML library](https://github.com/microsoft/poml) developed by Microsoft, which was originally implemented in JavaScript/TypeScript and Python. This Ruby gem is designed to be **fully compatible** with the original POML specification and will **closely follow** the development of the original library to maintain feature parity.
|
8
8
|
|
9
|
+
## Demo Video
|
10
|
+
|
11
|
+
[](https://youtu.be/b9WDcFsKixo)
|
12
|
+
|
9
13
|
### Original Library Resources
|
10
14
|
|
11
15
|
For comprehensive documentation, tutorials, and examples, please refer to the **original POML library documentation**:
|
12
16
|
|
13
17
|
- 📚 **Main Repository**: <https://github.com/microsoft/poml>
|
14
|
-
- 📖 **Documentation**: Complete language reference and guides
|
18
|
+
- 📖 **Documentation**: [Complete language reference and guides](https://microsoft.github.io/poml/latest/)
|
15
19
|
- 💡 **Examples**: Extensive collection of POML examples
|
16
20
|
- 🎯 **Use Cases**: Real-world applications and patterns
|
17
21
|
|
18
22
|
The original documentation is an excellent resource for learning POML concepts, syntax, and best practices that apply to this Ruby implementation as well.
|
19
23
|
|
24
|
+
## Implementation status
|
25
|
+
|
26
|
+
Please refer to [ROADMAP.md](https://github.com/GhennadiiMir/poml/blob/main/ROADMAP.md) for understanding which features are already implemented.
|
27
|
+
|
20
28
|
## Installation
|
21
29
|
|
22
30
|
```bash
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: poml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ghennadii Mirosnicenco
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-08-
|
10
|
+
date: 2025-08-18 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rexml
|
@@ -50,7 +50,6 @@ extensions: []
|
|
50
50
|
extra_rdoc_files: []
|
51
51
|
files:
|
52
52
|
- LICENSE.txt
|
53
|
-
- README.md
|
54
53
|
- TUTORIAL.md
|
55
54
|
- bin/poml
|
56
55
|
- examples/101_explain_character.poml
|
@@ -64,7 +63,6 @@ files:
|
|
64
63
|
- examples/202_arc_agi.poml
|
65
64
|
- examples/301_generate_poml.poml
|
66
65
|
- examples/README.md
|
67
|
-
- examples/_generate_expects.py
|
68
66
|
- examples/assets/101_jerry_mouse.jpg
|
69
67
|
- examples/assets/101_tom_and_jerry.docx
|
70
68
|
- examples/assets/101_tom_cat.jpg
|
@@ -104,11 +102,16 @@ files:
|
|
104
102
|
- lib/poml/components/content.rb
|
105
103
|
- lib/poml/components/data.rb
|
106
104
|
- lib/poml/components/examples.rb
|
105
|
+
- lib/poml/components/formatting.rb
|
107
106
|
- lib/poml/components/instructions.rb
|
108
107
|
- lib/poml/components/layout.rb
|
109
108
|
- lib/poml/components/lists.rb
|
109
|
+
- lib/poml/components/media.rb
|
110
|
+
- lib/poml/components/meta.rb
|
110
111
|
- lib/poml/components/styling.rb
|
112
|
+
- lib/poml/components/template.rb
|
111
113
|
- lib/poml/components/text.rb
|
114
|
+
- lib/poml/components/utilities.rb
|
112
115
|
- lib/poml/components/workflow.rb
|
113
116
|
- lib/poml/components_new.rb
|
114
117
|
- lib/poml/components_old.rb
|
@@ -119,6 +122,7 @@ files:
|
|
119
122
|
- lib/poml/version.rb
|
120
123
|
- media/logo-16-purple.png
|
121
124
|
- media/logo-64-white.png
|
125
|
+
- readme.md
|
122
126
|
homepage: https://github.com/GhennadiiMir/poml
|
123
127
|
licenses:
|
124
128
|
- MIT
|
@@ -1,35 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import poml
|
3
|
-
import io
|
4
|
-
import sys
|
5
|
-
from contextlib import redirect_stdout
|
6
|
-
|
7
|
-
|
8
|
-
def process_example(example_content, output_file):
|
9
|
-
"""
|
10
|
-
Process the example content and return the expected output.
|
11
|
-
"""
|
12
|
-
# Capture stdout
|
13
|
-
poml.poml(example_content, format="raw", output_file=output_file, extra_args=["--prettyPrint", "true"])
|
14
|
-
|
15
|
-
|
16
|
-
def generate_expectations():
|
17
|
-
"""
|
18
|
-
Generate the expected output files for the examples.
|
19
|
-
"""
|
20
|
-
examples_dir = os.path.abspath(os.path.dirname(__file__))
|
21
|
-
expect_dir = os.path.join(examples_dir, "expects")
|
22
|
-
print("Generating expectations in:", expect_dir)
|
23
|
-
|
24
|
-
for example_file in sorted(os.listdir(examples_dir)):
|
25
|
-
if example_file.endswith(".poml"):
|
26
|
-
print(f"Processing example: {example_file}")
|
27
|
-
# Generate the expected output
|
28
|
-
process_example(
|
29
|
-
os.path.join(examples_dir, example_file),
|
30
|
-
os.path.join(expect_dir, example_file.replace(".poml", ".txt")),
|
31
|
-
)
|
32
|
-
|
33
|
-
|
34
|
-
if __name__ == "__main__":
|
35
|
-
generate_expectations()
|