diff options
Diffstat (limited to 'logic/cartridge/parser.rb')
-rw-r--r-- | logic/cartridge/parser.rb | 134 |
1 files changed, 134 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 |