Skip to content

Instantly share code, notes, and snippets.

@fgimian
Created May 28, 2018 10:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fgimian/7ac343880045b8d303bf8b260d09cf59 to your computer and use it in GitHub Desktop.
Save fgimian/7ac343880045b8d303bf8b260d09cf59 to your computer and use it in GitHub Desktop.
The following Crystal code attempts to implement custom YAML tag processing
require "yaml"
# The default representation of null values in Crystal is an empty value which while valid
# (see http://yaml.org/type/null.html) but is less familiar and clear to me, use the string
# "null" instead
struct Nil
def to_yaml(yaml : YAML::Nodes::Builder)
yaml.scalar "null"
end
end
class FotsiesYAMLParser < YAML::Schema::Core::Parser
protected def parse_join
# TODO: what does anchor actually do? is it needed? e.g. anchor new_mapping
data = parse_sequence
# TODO: how do we add a value to the sequence and return that
# Error: undefined method '<<' for Bool (compile-time type is (Array(YAML::Any) | Bool | Float64 | Hash(YAML::Any, YAML::Any) | Int64 | Set(YAML::Any) | Slice(UInt8) | String | Time | Nil))
# value = data.raw
# value << YAML::Any.new("string")
# Scalars
value = "my string"
# value = 123.to_i64 # Integers must be 64-bit
# value = true
# value = 1234.46
# value = nil
# Arrays
# value = [] of YAML::Any
# value << YAML::Any.new("string")
# value << YAML::Any.new(123.to_i64)
# value << YAML::Any.new(123)
# value << YAML::Any.new(true)
# value << YAML::Any.new(123.5)
# value << YAML::Any.new(nil)
# Mappings
# value = {} of YAML::Any => YAML::Any
# value[YAML::Any.new("hi1")] = YAML::Any.new("string")
# value[YAML::Any.new("hi2")] = YAML::Any.new(123.to_i64)
# value[YAML::Any.new("hi3")] = YAML::Any.new(123)
# value[YAML::Any.new("hi4")] = YAML::Any.new(true)
# value[YAML::Any.new("hi5")] = YAML::Any.new(123.5)
# value[YAML::Any.new("hi6")] = YAML::Any.new(nil)
YAML::Any.new value
end
protected def parse_manipulate
data = parse_mapping
value = data.raw
YAML::Any.new value
end
protected def parse_upper
data = parse_scalar
YAML::Any.new(data.to_s.upcase)
end
private def process_fots_tag(pull_parser, tag)
# Checking the type of data beneath the tag
# puts "scalar: #{pull_parser.kind.scalar?}"
# puts "alias: #{pull_parser.kind.alias?}"
# puts "sequence_start: #{pull_parser.kind.sequence_start?}"
# puts "mapping_start: #{pull_parser.kind.mapping_start?}"
case tag
when "!join" # sequence
parse_join
when "!manipulate" # mapping
parse_manipulate
when "!upper" # scalar
parse_upper
else
nil
end
end
def process_tag(tag)
# TODO: how do we call the parent method here
# super
if value = process_fots_tag(@pull_parser, tag)
yield value
end
end
end
data = <<-END
---
types: [1, 1.2, abc, true, null]
joining: !join [a, b, c, d]
thingo: !manipulate
name: fots
age: 30
wow: !upper make me uppercase
END
data = FotsiesYAMLParser.new(data).parse
puts data.to_yaml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment