1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
# frozen_string_literal: true
require 'singleton'
require 'redcarpet'
require 'redcarpet/render_strip'
module NanoBot
module Logic
module Cartridge
module Parser
def self.parse(raw, format:)
normalized = format.to_s.downcase.gsub('.', '').strip
if %w[yml yaml].include?(normalized)
yaml(raw)
elsif %w[markdown mdown mkdn md].include?(normalized)
markdown(raw)
else
raise "Unknown cartridge format: '#{format}'"
end
end
def self.markdown(raw)
yaml_source = []
tools = []
blocks = Markdown.new.render(raw).blocks
previous_block_is_tool = false
blocks.each do |block|
if block[:language] == 'yaml'
parsed = Logic::Helpers::Hash.symbolize_keys(
YAML.safe_load(block[:source], permitted_classes: [Symbol])
)
if parsed.key?(:tools) && parsed[:tools].is_a?(Array) && !parsed[:tools].empty?
previous_block_is_tool = true
tools.concat(parsed[:tools])
parsed.delete(:tools)
unless parsed.empty?
yaml_source << YAML.dump(
Logic::Helpers::Hash.stringify_keys(parsed)
).gsub(/^---/, '') # TODO: Is this safe enough?
end
else
yaml_source << block[:source]
previous_block_is_tool = false
nil
end
elsif previous_block_is_tool
tools.last[block[:language].to_sym] = block[:source]
previous_block_is_tool = false
end
end
unless tools.empty?
yaml_source << YAML.dump(
Logic::Helpers::Hash.stringify_keys({ tools: })
).gsub(/^---/, '') # TODO: Is this safe enough?
end
cartridge = {}
yaml_source.each do |source|
cartridge = Logic::Helpers::Hash.deep_merge(cartridge, yaml(source))
end
cartridge
end
def self.yaml(raw)
Logic::Helpers::Hash.symbolize_keys(
YAML.safe_load(raw, permitted_classes: [Symbol])
)
end
class Renderer < Redcarpet::Render::Base
LANGUAGES_MAP = {
'yml' => 'yaml',
'yaml' => 'yaml',
'lua' => 'lua',
'fnl' => 'fennel',
'fennel' => 'fennel',
'clj' => 'clojure',
'clojure' => 'clojure'
}.freeze
LANGUAGES = LANGUAGES_MAP.keys.freeze
def initialize(...)
super(...)
@_nano_bots_blocks = []
end
attr_reader :_nano_bots_blocks
def block_code(code, language)
key = language.to_s.downcase.strip
return nil unless LANGUAGES.include?(key)
@_nano_bots_blocks << { language: LANGUAGES_MAP[key], source: code }
nil
end
end
class Markdown
attr_reader :markdown
def initialize
@renderer = Renderer.new
@markdown = Redcarpet::Markdown.new(@renderer, fenced_code_blocks: true)
end
def blocks
@renderer._nano_bots_blocks
end
def render(raw)
@markdown.render(raw.gsub(/```\w/, "\n\n\\0"))
self
end
end
end
end
end
end
|