#!/usr/bin/ruby # The MIT License (MIT) # # Copyright © 2024 pacman64 # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. info = <<-EOF tyr [options...] [ruby expression] [filepath/URI...] Transform Yaml with Ruby loads YAML data, runs a Ruby expression on it, and emits the result as YAML. Parsed input-data are available to the expression as any of the variables named `v`, `value`, `d`, and `data`. If no file/URI is given, it loads YAML data from its standard input. If the argument before the expression is a single equals sign (a `=`, without the quotes), no data are read/parsed, and the expression is evaluated as given. Options, where leading double-dashes are also allowed, except for alias `=`: -h show this help message -help same as -h -nil don't read any input -no-input same as -nil -noinput same as -nil -none same as -nil -null same as -nil = same as -nil -t show a full traceback of this script for exceptions -trace same as -t -traceback same as -t EOF if ARGV.any? { |e| ['-h', '--h', '-help', '--help'].include?(e) } STDERR.print(info) exit(0) end require 'random/formatter' require 'set' require 'uri' require 'yaml' def self.open(name, &block) throw 'global function `open` is disabled' end def load_yaml(name) require 'open-uri' if name == '-' return YAML.load(STDIN) else return URI.open(name) { |r| YAML.load(r) } end end def fix_result(res, value) if res.respond_to?(:call) return res(value) end # if res.class == Hash # can_go_deep = res.respond_to?(:deep_stringify_keys) # return can_go_deep ? res.deep_stringify_keys : res.stringify_keys # end return res end def main() expression = nil load_input = true traceback = false input_name = nil no_input_opts = [ '=', '-nil', '--nil', '-none', '--none', '-null', '--null', '-noinput', '-noinput', '-no-input', '--no-input', ] traceback_opts = [ '-t', '--t', '-trace', '--trace', '-traceback', '--traceback', ] ARGV.each do |e| if no_input_opts.include?(e) load_input = false elsif traceback_opts.include?(e) traceback = true elsif expression.nil? expression = e elsif input_name.nil? input_name = e else STDERR.print("\x1b[31mgiven too many arguments\x1b[0m\n") exit(1) end end if expression.nil? STDERR.print("\x1b[31mno transformation expression given\x1b[0m\n") exit(1) end begin if !load_input data = nil elsif input_name.nil? || input_name == '-' data = YAML.load(STDIN) else data = load_yaml(input_name) end # using the global vars env because of all the extras defined in it TOPLEVEL_BINDING.local_variable_set(:d, data) TOPLEVEL_BINDING.local_variable_set(:data, data) TOPLEVEL_BINDING.local_variable_set(:v, data) TOPLEVEL_BINDING.local_variable_set(:val, data) TOPLEVEL_BINDING.local_variable_set(:value, data) if expression.strip != '.' data = fix_result(TOPLEVEL_BINDING.eval(expression), data) end YAML.dump(data, STDOUT) STDOUT.print("\n") rescue => e if traceback throw e end STDERR.print("\x1b[31m#{e}\x1b[0m\n") exit(1) end end d = nil data = nil v = nil val = nil value = nil nihil = nil none = nil null = nil s = '' months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] monweek = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', ] sunweek = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ] module Dottable def method_missing(m, *args, &block) Hash(self)[m] end end phy = { 'kilo': 1_000, 'mega': 1_000_000, 'giga': 1_000_000_000, 'tera': 1_000_000_000_000, 'peta': 1_000_000_000_000_000, 'exa': 1_000_000_000_000_000_000, 'zetta': 1_000_000_000_000_000_000_000, 'c': 299_792_458, 'kcd': 683, 'na': 602214076000000000000000, 'femto': 1e-15, 'pico': 1e-12, 'nano': 1e-9, 'micro': 1e-6, 'milli': 1e-3, 'e': 1.602176634e-19, 'f': 96_485.33212, 'h': 6.62607015e-34, 'k': 1.380649e-23, 'mu': 1.66053906892e-27, 'ge': 9.7803267715, 'gn': 9.80665, } phy.extend(Dottable) physics = phy # using literal strings on the cmd-line is often tricky/annoying: some of # these aliases can help get around multiple levels of string-quoting; no # quotes are needed as the script will later make these values accessible # via the property/dot syntax sym = { 'amp': '&', 'ampersand': '&', 'ansiclear': '\x1b[0m', 'ansinormal': '\x1b[0m', 'ansireset': '\x1b[0m', 'apo': '\'', 'apos': '\'', 'ast': '*', 'asterisk': '*', 'at': '@', 'backquote': '`', 'backslash': '\\', 'backtick': '`', 'ball': '●', 'bang': '!', 'bigsigma': 'Σ', 'block': '█', 'bquo': '`', 'bquote': '`', 'bslash': '\\', 'bullet': '•', 'caret': '^', 'cdot': '·', 'circle': '●', 'colon': ':', 'comma': ',', 'cr': '\r', 'crlf': '\r\n', 'cross': '×', 'cs': ', ', # 'dash': '–', 'dash': '—', 'dollar': '$', 'dot': '.', 'dquo': '"', 'dquote': '"', 'emark': '!', 'empty': '', 'eq': '=', 'et': '&', 'euro': '€', 'ge': '≥', 'geq': '≥', 'gt': '>', 'hellip': '…', 'hole': '○', 'infinity': '∞', 'le': '≤', 'leq': '≤', 'lf': '\n', 'lt': '<', 'mdash': '—', 'mdot': '·', 'miniball': '•', 'minus': '-', 'ndash': '–', 'neq': '≠', 'perc': '%', 'percent': '%', 'period': '.', 'plus': '+', 'qmark': '?', 'que': '?', 'sball': '•', 'semi': ';', 'semicolon': ';', 'sharp': '#', 'slash': '/', 'space': ' ', 'square': '■', 'squo': '\'', 'squote': '\'', 'tab': '\t', 'tilde': '~', 'underscore': '_', 'uscore': '_', 'utf8bom': '\xef\xbb\xbf', } sym.extend(Dottable) symbols = sym units = { 'cup2l': 0.23658824, 'floz2l': 0.0295735295625, 'floz2ml': 29.5735295625, 'ft2m': 0.3048, 'gal2l': 3.785411784, 'in2cm': 2.54, 'lb2kg': 0.45359237, 'mi2km': 1.609344, 'mpg2kpl': 0.425143707, 'nmi2km': 1.852, 'oz2g': 28.34952312, 'psi2pa': 6894.757293168, 'ton2kg': 907.18474, 'yd2m': 0.9144, 'mol': 602214076000000000000000, 'mole': 602214076000000000000000, 'hour': 3_600, 'day': 86_400, 'week': 604_800, 'hr': 3_600, 'wk': 604_800, 'kb': 1024, 'mb': 1024**2, 'gb': 1024**3, 'tb': 1024**4, 'pb': 1024**5, } units.extend(Dottable) main()