M lib/dhall/ast.rb => lib/dhall/ast.rb +214 -1
@@ 110,6 110,10 @@ module Dhall
function Expression
arguments Util::ArrayOf.new(Expression, min: 1)
end)
+
+ def as_json
+ [0, function.as_json, *arguments.map(&:as_json)]
+ end
end
class Function < Expression
@@ 137,9 141,25 @@ module Dhall
args.first.shift(1, var, 0)
).shift(-1, var, 0).normalize
end
+
+ def as_json
+ if var == "_"
+ [1, type.as_json, body.as_json]
+ else
+ [1, var, type.as_json, body.as_json]
+ end
+ end
end
- class Forall < Function; end
+ class Forall < Function
+ def as_json
+ if var == "_"
+ [2, type.as_json, body.as_json]
+ else
+ [2, var, type.as_json, body.as_json]
+ end
+ end
+ end
class Bool < Expression
include(ValueSemantics.for_attributes do
@@ 161,6 181,10 @@ module Dhall
def dhall_eq(other)
reduce(other, super)
end
+
+ def as_json
+ value
+ end
end
class Variable < Expression
@@ 172,6 196,16 @@ module Dhall
def self.[](name, index=0)
new(name: name, index: index)
end
+
+ def as_json
+ if name == "_"
+ index
+ elsif index == 0
+ name
+ else
+ [name, index]
+ end
+ end
end
class Operator < Expression
@@ 180,6 214,10 @@ module Dhall
rhs Expression
end)
+ def as_json
+ [3, OPERATORS.index(self.class), lhs.as_json, rhs.as_json]
+ end
+
class Or < Operator; end
class And < Operator; end
class Equal < Operator; end
@@ 192,6 230,14 @@ module Dhall
class RightBiasedRecordMerge < Operator; end
class RecursiveRecordTypeMerge < Operator; end
class ImportFallback < Operator; end
+
+ OPERATORS = [
+ Or, And, Equal, NotEqual,
+ Plus, Times,
+ TextConcatenate, ListConcatenate,
+ RecursiveRecordMerge, RightBiasedRecordMerge, RecursiveRecordTypeMerge,
+ ImportFallback
+ ].freeze
end
class List < Expression
@@ 203,6 249,10 @@ module Dhall
List.new(elements: args)
end
+ def as_json
+ [4, nil, *elements.map(&:as_json)]
+ end
+
def type
# TODO: inferred element type
end
@@ 245,6 295,10 @@ module Dhall
type Expression
end)
+ def as_json
+ [4, type.as_json]
+ end
+
def map(type: nil)
type.nil? ? self : with(type: type)
end
@@ 283,6 337,10 @@ module Dhall
def reduce(_, &block)
block[value]
end
+
+ def as_json
+ [5, type&.as_json, value.as_json]
+ end
end
class OptionalNone < Optional
@@ 293,6 351,10 @@ module Dhall
def reduce(z)
z
end
+
+ def as_json
+ [5, type.as_json]
+ end
end
class Merge < Expression
@@ 301,6 363,11 @@ module Dhall
input Expression
type Either(Expression, nil)
end)
+
+ def as_json
+ [6, record.as_json, input.as_json] +
+ (type.nil? ? [] : [type.as_json])
+ end
end
class RecordType < Expression
@@ 323,6 390,10 @@ module Dhall
def eql?(other)
self == other
end
+
+ def as_json
+ [7, Hash[record.to_a.map { |k, v| [k, v.as_json] }.sort]]
+ end
end
class EmptyRecordType < Expression
@@ 331,6 402,10 @@ module Dhall
def deep_merge_type(other)
other
end
+
+ def as_json
+ [7, {}]
+ end
end
class Record < Expression
@@ 371,6 446,10 @@ module Dhall
def eql?(other)
self == other
end
+
+ def as_json
+ [8, Hash[record.to_a.map { |k, v| [k, v.as_json] }.sort]]
+ end
end
class EmptyRecord < Expression
@@ 391,6 470,10 @@ module Dhall
def merge(other)
other
end
+
+ def as_json
+ [8, {}]
+ end
end
class RecordSelection < Expression
@@ 398,6 481,10 @@ module Dhall
record Expression
selector ::String
end)
+
+ def as_json
+ [9, record.as_json, selector]
+ end
end
class RecordProjection < Expression
@@ 405,12 492,20 @@ module Dhall
record Expression
selectors Util::ArrayOf.new(::String, min: 1)
end)
+
+ def as_json
+ [10, record.as_json, *selectors]
+ end
end
class EmptyRecordProjection < Expression
include(ValueSemantics.for_attributes do
record Expression
end)
+
+ def as_json
+ [10, record.as_json]
+ end
end
class UnionType < Expression
@@ 438,6 533,10 @@ module Dhall
)
)
end
+
+ def as_json
+ [11, Hash[alternatives.to_a.map { |k, v| [k, v.as_json] }.sort]]
+ end
end
class Union < Expression
@@ 446,6 545,10 @@ module Dhall
value Expression
alternatives UnionType
end)
+
+ def as_json
+ [12, tag, value.as_json, alternatives.as_json.last]
+ end
end
class If < Expression
@@ 454,6 557,10 @@ module Dhall
self.then Expression
self.else Expression
end)
+
+ def as_json
+ [14, predicate.as_json, self.then.as_json, self.else.as_json]
+ end
end
class Number < Expression
@@ 500,6 607,10 @@ module Dhall
def pred
with(value: [0, value - 1].max)
end
+
+ def as_json
+ [15, value]
+ end
end
class Integer < Number
@@ 510,6 621,10 @@ module Dhall
def to_s
"#{value >= 0 ? "+" : ""}#{value}"
end
+
+ def as_json
+ [16, value]
+ end
end
class Double < Number
@@ 520,6 635,34 @@ module Dhall
def to_s
value.to_s
end
+
+ def single?
+ [value].pack("g").unpack("g").first == value
+ end
+
+ def as_json
+ value
+ end
+
+ def as_cbor
+ self
+ end
+
+ def to_cbor(packer=nil)
+ if [0.0, Float::INFINITY, -Float::INFINITY].include?(value) ||
+ value.nan?
+ return value.to_cbor(packer)
+ end
+
+ # Dhall spec requires *not* using half-precision CBOR floats
+ bytes = single? ? [0xFA, value].pack("Cg") : [0xFB, value].pack("CG")
+ if packer
+ packer.buffer.write(bytes)
+ packer
+ else
+ bytes
+ end
+ end
end
class Text < Expression
@@ 534,12 677,21 @@ module Dhall
super
end
end
+
+ def as_json
+ [18, value]
+ end
end
class TextLiteral < Expression
include(ValueSemantics.for_attributes do
chunks ArrayOf(Expression)
end)
+
+ def as_json
+ raise "TextLiteral must start with a Text" unless chunks.first.is_a?(Text)
+ [18, *chunks.map { |chunk| chunk.is_a?(Text) ? chunk.value : chunk.as_json }]
+ end
end
class Import < Expression
@@ 549,6 701,16 @@ module Dhall
@path = path
end
+ def as_json
+ [
+ 24,
+ @integrity_check&.as_json,
+ IMPORT_TYPES.index(@import_type),
+ PATH_TYPES.index(@path.class),
+ *@path.as_json
+ ]
+ end
+
class URI
include(ValueSemantics.for_attributes do
headers Either(nil, Expression)
@@ 581,6 743,10 @@ module Dhall
def uri
URI("#{scheme}://#{authority}/#{path.join("/")}?#{query}")
end
+
+ def as_json
+ [headers.as_json, authority, *path, query, fragment]
+ end
end
class Http < URI
@@ 630,6 796,10 @@ module Dhall
def to_s
pathname.to_s
end
+
+ def as_json
+ path
+ end
end
class AbsolutePath < Path
@@ 677,12 847,20 @@ module Dhall
Path.from_string(val)
end.resolve(resolver)
end
+
+ def as_json
+ var
+ end
end
class MissingImport
def resolve(*)
Promise.new.reject(ImportFailedException.new("missing"))
end
+
+ def as_json
+ []
+ end
end
class IntegrityCheck
@@ 691,6 869,29 @@ module Dhall
@data = data
end
end
+
+ class Expression
+ def self.call(import_value)
+ Dhall.from_binary(import_value)
+ end
+ end
+
+ class Text
+ def self.call(import_value)
+ Dhall::Text.new(value: import_value)
+ end
+ end
+
+ IMPORT_TYPES = [
+ Expression,
+ Text
+ ].freeze
+
+ PATH_TYPES = [
+ Http, Https,
+ AbsolutePath, RelativePath, RelativeToParentPath, RelativeToHomePath,
+ EnvironmentVariable, MissingImport
+ ].freeze
end
class Let < Expression
@@ 699,6 900,10 @@ module Dhall
assign Expression
type Either(nil, Expression)
end)
+
+ def as_json
+ [var, type&.as_json, assign.as_json]
+ end
end
class LetBlock < Expression
@@ 706,6 911,10 @@ module Dhall
lets ArrayOf(Let)
body Expression
end)
+
+ def as_json
+ [25, *lets.flat_map(&:as_json), body.as_json]
+ end
end
class TypeAnnotation < Expression
@@ 713,5 922,9 @@ module Dhall
value Expression
type Expression
end)
+
+ def as_json
+ [26, value.as_json, type.as_json]
+ end
end
end
M lib/dhall/binary.rb => lib/dhall/binary.rb +9 -19
@@ 24,6 24,15 @@ module Dhall
new(*args)
end
+
+ def to_cbor(io=nil)
+ CBOR.encode(as_cbor, io)
+ end
+ alias to_binary to_cbor
+
+ def as_cbor
+ as_json
+ end
end
class Application
@@ 51,14 60,6 @@ module Dhall
end
class Operator
- OPERATORS = [
- Or, And, Equal, NotEqual,
- Plus, Times,
- TextConcatenate, ListConcatenate,
- RecursiveRecordMerge, RightBiasedRecordMerge, RecursiveRecordTypeMerge,
- ImportFallback
- ].freeze
-
def self.decode(opcode, lhs, rhs)
OPERATORS[opcode].new(
lhs: Dhall.decode(lhs),
@@ 176,17 177,6 @@ module Dhall
end
class Import
- IMPORT_TYPES = [
- Dhall.method(:from_binary),
- ->(x) { Text.new(value: x) }
- ].freeze
-
- PATH_TYPES = [
- Http, Https,
- AbsolutePath, RelativePath, RelativeToParentPath, RelativeToHomePath,
- EnvironmentVariable, MissingImport
- ].freeze
-
def self.decode(integrity_check, import_type, path_type, *parts)
parts[0] = Dhall.decode(parts[0]) if path_type < 2 && !parts[0].nil?
M lib/dhall/builtins.rb => lib/dhall/builtins.rb +4 -0
@@ 13,6 13,10 @@ module Dhall
end
end
+ def as_json
+ self.class.name.split(/::/).last.gsub(/_/, "/")
+ end
+
protected
def attributes
A test/test_as_json.rb => test/test_as_json.rb +23 -0
@@ 0,0 1,23 @@
+# frozen_string_literal: true
+
+require "minitest/autorun"
+require "pathname"
+require "cbor"
+
+require "dhall/ast"
+require "dhall/binary"
+
+class TestAsJson < Minitest::Test
+ DIRPATH = Pathname.new(File.dirname(__FILE__))
+ TESTS = DIRPATH + "normalization/"
+
+ Pathname.glob(TESTS + "**/*.dhallb").each do |path|
+ test = path.relative_path_from(TESTS).to_s.sub(/.dhallb$/, "")
+ define_method("test_#{test}") do
+ assert_equal(
+ CBOR.decode(path.read).inspect,
+ Dhall.from_binary(path.read).as_json.inspect
+ )
+ end
+ end
+end
M test/test_binary.rb => test/test_binary.rb +8 -5
@@ 6,14 6,17 @@ require "pathname"
require "dhall/ast"
require "dhall/binary"
-class TestParser < Minitest::Test
+class TestBinary < Minitest::Test
DIRPATH = Pathname.new(File.dirname(__FILE__))
- TESTS = DIRPATH + "../dhall-lang/tests/parser/success/"
+ TESTS = DIRPATH + "normalization/"
- Pathname.glob(TESTS + "*B.dhallb").each do |path|
- test = path.basename("B.dhallb").to_s
+ Pathname.glob(TESTS + "**/*.dhallb").each do |path|
+ test = path.relative_path_from(TESTS).to_s.sub(/.dhallb$/, "")
define_method("test_#{test}") do
- assert_kind_of Dhall::Expression, Dhall.from_binary(path.read)
+ assert_equal(
+ path.binread,
+ Dhall.from_binary(path.binread).to_binary
+ )
end
end
end
M test/test_normalization.rb => test/test_normalization.rb +2 -2
@@ 20,8 20,8 @@ class TestNormalization < Minitest::Test
define_method("test_#{test.gsub(/\//, "_")}") do
Dhall::Function.disable_alpha_normalization! if test =~ /^standard\//
assert_equal(
- Dhall.from_binary(TESTS + "#{test}B.dhallb"),
- Dhall.from_binary(path.read).normalize
+ Dhall.from_binary((TESTS + "#{test}B.dhallb").binread),
+ Dhall.from_binary(path.binread).normalize
)
Dhall::Function.enable_alpha_normalization! if test =~ /^standard\//
end
M test/test_resolve.rb => test/test_resolve.rb +14 -14
@@ 43,7 43,7 @@ class TestResolve < Minitest::Test
def test_import_as_text
expr = Dhall::Import.new(
nil,
- ->(x) { Dhall::Text.new(value: x) },
+ Dhall::Import::Text,
Dhall::Import::RelativePath.new("text")
)
@@ 58,7 58,7 @@ class TestResolve < Minitest::Test
Dhall::Variable["Natural"],
body: Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::RelativePath.new("var")
)
)
@@ 74,7 74,7 @@ class TestResolve < Minitest::Test
Dhall::Variable["Natural"],
body: Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::RelativePath.new("import")
)
)
@@ 90,7 90,7 @@ class TestResolve < Minitest::Test
Dhall::Variable["Natural"],
body: Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::RelativePath.new("self")
)
)
@@ 105,7 105,7 @@ class TestResolve < Minitest::Test
Dhall::Variable["Natural"],
body: Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::RelativePath.new("a")
)
)
@@ 118,7 118,7 @@ class TestResolve < Minitest::Test
def test_two_references_no_loop
expr = Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::RelativePath.new("2text")
)
@@ 134,7 134,7 @@ class TestResolve < Minitest::Test
def test_missing
expr = Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::MissingImport.new
)
@@ 147,7 147,7 @@ class TestResolve < Minitest::Test
expr = Dhall::Operator::ImportFallback.new(
lhs: Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::MissingImport.new
),
rhs: Dhall::Variable["fallback"]
@@ 160,12 160,12 @@ class TestResolve < Minitest::Test
expr = Dhall::Operator::ImportFallback.new(
lhs: Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::MissingImport.new
),
rhs: Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::RelativePath.new("import")
)
)
@@ 179,7 179,7 @@ class TestResolve < Minitest::Test
expr = Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::AbsolutePath.new("ipfs", "TESTCID")
)
@@ 195,7 195,7 @@ class TestResolve < Minitest::Test
expr = Dhall::Import.new(
nil,
- Dhall.method(:from_binary),
+ Dhall::Import::Expression,
Dhall::Import::AbsolutePath.new("ipfs", "TESTCID")
)
@@ 211,8 211,8 @@ class TestResolve < Minitest::Test
define_method("test_#{test.gsub(/\//, "_")}") do
Dhall::Function.disable_alpha_normalization! if test =~ /^standard\//
assert_equal(
- Dhall.from_binary(TESTS + "#{test}B.dhallb"),
- Dhall.from_binary(path.read).resolve.sync.normalize
+ Dhall.from_binary((TESTS + "#{test}B.dhallb").binread),
+ Dhall.from_binary(path.binread).resolve.sync.normalize
)
Dhall::Function.enable_alpha_normalization! if test =~ /^standard\//
end