diff options
Diffstat (limited to 'logic')
-rw-r--r-- | logic/cartridge/parser.rb | 134 | ||||
-rw-r--r-- | logic/helpers/hash.rb | 23 |
2 files changed, 157 insertions, 0 deletions
diff --git a/logic/cartridge/parser.rb b/logic/cartridge/parser.rb new file mode 100644 index 0000000..50b3dc5 --- /dev/null +++ b/logic/cartridge/parser.rb @@ -0,0 +1,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 diff --git a/logic/helpers/hash.rb b/logic/helpers/hash.rb index 90432b5..66b6742 100644 --- a/logic/helpers/hash.rb +++ b/logic/helpers/hash.rb @@ -4,6 +4,16 @@ module NanoBot module Logic module Helpers module Hash + def self.deep_merge(hash1, hash2) + hash1.merge(hash2) do |_key, old_val, new_val| + if old_val.is_a?(::Hash) && new_val.is_a?(::Hash) + deep_merge(old_val, new_val) + else + new_val + end + end + end + def self.symbolize_keys(object) case object when ::Hash @@ -17,6 +27,19 @@ module NanoBot end end + def self.stringify_keys(object) + case object + when ::Hash + object.each_with_object({}) do |(key, value), result| + result[key.to_s] = stringify_keys(value) + end + when Array + object.map { |e| stringify_keys(e) } + else + object + end + end + def self.fetch(object, path) node = object |