Class: Odin::Transform::TransformParser

Inherits:
Object
  • Object
show all
Defined in:
lib/odin/transform/transform_parser.rb

Defined Under Namespace

Classes: ParseError

Constant Summary collapse

VERB_ARITY =

── Complete Verb Arity Table ──

{
  # Arity 0
  "today" => 0, "now" => 0,

  # Arity 1
  "upper" => 1, "lower" => 1, "trim" => 1, "trimLeft" => 1, "trimRight" => 1,
  "coerceString" => 1, "coerceNumber" => 1, "coerceInteger" => 1,
  "coerceBoolean" => 1, "coerceDate" => 1, "coerceTimestamp" => 1,
  "tryCoerce" => 1, "toArray" => 1, "toObject" => 1,
  "not" => 1, "isNull" => 1, "isString" => 1, "isNumber" => 1,
  "isBoolean" => 1, "isArray" => 1, "isObject" => 1, "isDate" => 1,
  "typeOf" => 1,
  "capitalize" => 1, "titleCase" => 1, "length" => 1, "reverseString" => 1,
  "camelCase" => 1, "snakeCase" => 1, "kebabCase" => 1, "pascalCase" => 1,
  "slugify" => 1, "normalizeSpace" => 1, "stripAccents" => 1, "clean" => 1,
  "wordCount" => 1, "soundex" => 1,
  "abs" => 1, "floor" => 1, "ceil" => 1, "negate" => 1, "sign" => 1,
  "trunc" => 1, "isFinite" => 1, "isNaN" => 1, "ln" => 1, "log10" => 1,
  "exp" => 1, "sqrt" => 1,
  "formatInteger" => 1, "formatCurrency" => 1,
  "startOfDay" => 1, "endOfDay" => 1, "startOfMonth" => 1, "endOfMonth" => 1,
  "startOfYear" => 1, "endOfYear" => 1, "dayOfWeek" => 1, "weekOfYear" => 1,
  "quarter" => 1, "isLeapYear" => 1, "toUnix" => 1, "fromUnix" => 1,
  "dayOfMonth" => 1, "dayOfYear" => 1,
  "base64Encode" => 1, "base64Decode" => 1, "urlEncode" => 1, "urlDecode" => 1,
  "jsonEncode" => 1, "jsonDecode" => 1, "hexEncode" => 1, "hexDecode" => 1,
  "sha256" => 1, "sha1" => 1, "sha512" => 1, "md5" => 1, "crc32" => 1,
  "flatten" => 1, "distinct" => 1, "sort" => 1, "sortDesc" => 1,
  "reverse" => 1, "compact" => 1, "unique" => 1, "cumsum" => 1, "cumprod" => 1,
  "sum" => 1, "count" => 1, "min" => 1, "max" => 1, "avg" => 1,
  "first" => 1, "last" => 1,
  "std" => 1, "stdSample" => 1, "variance" => 1, "varianceSample" => 1,
  "median" => 1, "mode" => 1, "rowNumber" => 1,
  "uuid" => 1, "sequence" => 1, "resetSequence" => 1,
  "keys" => 1, "values" => 1, "entries" => 1,
  "toRadians" => 1, "toDegrees" => 1,
  "nextBusinessDay" => 1, "formatDuration" => 1,

  # Arity 2
  "ifNull" => 2, "ifEmpty" => 2,
  "and" => 2, "or" => 2, "xor" => 2,
  "eq" => 2, "ne" => 2, "lt" => 2, "lte" => 2, "gt" => 2, "gte" => 2,
  "contains" => 2, "startsWith" => 2, "endsWith" => 2,
  "truncate" => 2, "join" => 2,
  "mask" => 2, "match" => 2, "leftOf" => 2, "rightOf" => 2,
  "repeat" => 2, "matches" => 2, "levenshtein" => 2, "tokenize" => 2,
  "add" => 2, "subtract" => 2, "multiply" => 2, "divide" => 2, "mod" => 2,
  "formatNumber" => 2, "pow" => 2, "log" => 2, "formatPercent" => 2,
  "parseInt" => 2, "formatLocaleNumber" => 2, "round" => 2,
  "formatDate" => 2, "parseDate" => 2,
  "addDays" => 2, "addMonths" => 2, "addYears" => 2,
  "addHours" => 2, "addMinutes" => 2, "addSeconds" => 2,
  "formatTime" => 2, "formatTimestamp" => 2, "parseTimestamp" => 2,
  "isBefore" => 2, "isAfter" => 2,
  "daysBetweenDates" => 2, "ageFromDate" => 2, "isValidDate" => 2,
  "formatLocaleDate" => 2,
  "accumulate" => 2, "set" => 2,
  "percentile" => 2, "quantile" => 2, "covariance" => 2, "correlation" => 2,
  "weightedAvg" => 2, "npv" => 2, "irr" => 2, "zscore" => 2,
  "sortBy" => 2, "map" => 2, "indexOf" => 2, "at" => 2,
  "includes" => 2, "concatArrays" => 2, "zip" => 2, "groupBy" => 2,
  "take" => 2, "drop" => 2, "chunk" => 2, "pluck" => 2,
  "dedupe" => 2, "diff" => 2, "pctChange" => 2, "limit" => 2,
  "nanoid" => 2,
  "has" => 2, "merge" => 2, "jsonPath" => 2,
  "assert" => 2,
  "formatPhone" => 2, "movingAvg" => 2, "businessDays" => 2,
  "fromEntries" => 1, "invert" => 1, "defaults" => 2, "renameKeys" => 2,
  "compactObject" => 1,
  "intersection" => 2, "union" => 2, "difference" => 2,
  "symmetricDifference" => 2, "countBy" => 2, "keyBy" => 2,
  "explode" => 2, "window" => 2,
  "base64urlEncode" => 1, "base64urlDecode" => 1, "hmac" => 3,
  "parseUrl" => 1, "buildUrl" => 1, "parseQuery" => 1, "buildQuery" => 1,
  "stableStringify" => 1, "canonicalHash" => 1,
  "escapeHtml" => 1, "unescapeHtml" => 1, "escapeXml" => 1, "stripTags" => 1,
  "template" => 2,
  "gcd" => 2, "lcm" => 2, "factorial" => 1,
  "expr" => 2,
  "countIf" => 4, "sumIf" => 5, "avgIf" => 5,
  "xnpv" => 3, "xirr" => 3,

  # Arity 3
  "ifElse" => 3, "between" => 3,
  "substring" => 3, "replace" => 3, "replaceRegex" => 3,
  "padLeft" => 3, "padRight" => 3, "pad" => 3,
  "split" => 3, "extract" => 3, "wrap" => 3, "center" => 3,
  "clamp" => 3, "random" => 3, "safeDivide" => 3,
  "dateDiff" => 3, "isBetween" => 3,
  "compound" => 3, "discount" => 3, "pmt" => 3, "fv" => 3, "pv" => 3,
  "depreciation" => 3,
  "slice" => 3, "range" => 3, "shift" => 3, "rank" => 3,
  "lag" => 3, "lead" => 3, "sample" => 3, "fillMissing" => 3,
  "get" => 3,
  "reduce" => 3, "pivot" => 3, "unpivot" => 3, "convertUnit" => 3,

  # Arity 4
  "rate" => 4, "nper" => 4,
  "filter" => 4, "every" => 4, "some" => 4, "find" => 4,
  "findIndex" => 4, "partition" => 4,
  "bearing" => 4, "midpoint" => 4,

  # Arity 5
  "distance" => 5, "interpolate" => 5,

  # Arity 6
  "inBoundingBox" => 6
}.freeze
VARIADIC_VERBS =
%w[
  concat coalesce cond switch lookup lookupDefault minOf maxOf pick omit
].freeze
ALL_DIRECTIVES =
%w[
  pos len field trim type date time timestamp boolean integer number
  currency percent binary duration reference leftPad rightPad truncate
  upper lower default decimals currencyCode required confidential
  deprecated attr ns if unless omitNull omitEmpty
].freeze

Instance Method Summary collapse

Instance Method Details

#parse(text) ⇒ Object

Raises:



137
138
139
140
141
142
143
# File 'lib/odin/transform/transform_parser.rb', line 137

def parse(text)
  raise ParseError.new("Transform text cannot be nil", code: "T001") if text.nil?
  raise ParseError.new("Transform text cannot be empty", code: "T001") if text.strip.empty?

  lines = text.lines.map(&:chomp)
  parse_lines(lines)
end

#parse_expression_string(raw) ⇒ Object

Public for testing expression parsing



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/odin/transform/transform_parser.rb', line 146

def parse_expression_string(raw)
  return [LiteralExpr.new(Types::DynValue.of_null), []] if raw.nil? || raw.strip.empty?

  raw = raw.strip
  tokens = tokenize_expression(raw)
  return [LiteralExpr.new(Types::DynValue.of_null), []] if tokens.empty?

  expr, remaining = parse_expr_from_tokens(tokens)
  directives = parse_directives(remaining)
  [expr, directives]
end