M lib/dhall/ast.rb => lib/dhall/ast.rb +16 -0
@@ 129,6 129,14 @@ module Dhall
def map_subexpressions(&block)
with(var: var, type: type.nil? ? nil : block[type], body: block[body])
end
+
+ def call(*args)
+ return super if args.length > 1
+ body.substitute(
+ Variable.new(name: var),
+ args.first.shift(1, var, 0)
+ ).shift(-1, var, 0).normalize
+ end
end
class Forall < Function; end
@@ 279,6 287,10 @@ module Dhall
def map_subexpressions(&block)
with(value: block[value], type: type.nil? ? type : block[type])
end
+
+ def reduce(_, &block)
+ block[value]
+ end
end
class OptionalNone < Optional
@@ 289,6 301,10 @@ module Dhall
def map_subexpressions(&block)
with(type: block[@type])
end
+
+ def reduce(z)
+ z
+ end
end
class Merge < Expression
M lib/dhall/builtins.rb => lib/dhall/builtins.rb +84 -0
@@ 154,6 154,10 @@ module Dhall
end
class List_build < Builtin
+ def initialize(type=nil)
+ @type = type
+ end
+
def fusion(*args)
_, arg, = args
if arg.is_a?(Application) &&
@@ 165,6 169,37 @@ module Dhall
super
end
end
+
+ def call(arg)
+ if @type.nil?
+ self.class.new(arg)
+ else
+ arg.call(
+ Application.new(
+ function: Variable.new(name: "List"),
+ arguments: [@type]
+ ),
+ Function.new(
+ var: "_",
+ type: @type,
+ body: Function.new(
+ var: "_",
+ type: Application.new(
+ function: Variable.new(name: "List"),
+ arguments: [@type.shift(1, "_", 0)]
+ ),
+ body: Operator::ListConcatenate.new(
+ lhs: List.new(
+ elements: [Variable.new(name: "_", index: 1)]
+ ),
+ rhs: Variable.new(name: "_")
+ )
+ ),
+ ),
+ EmptyList.new(type: @type)
+ )
+ end
+ end
end
class List_fold < Builtin
@@ 249,6 284,10 @@ module Dhall
end
class Optional_build < Builtin
+ def initialize(type=nil)
+ @type = type
+ end
+
def fusion(*args)
_, arg, = args
if arg.is_a?(Application) &&
@@ 260,9 299,54 @@ module Dhall
super
end
end
+
+ def call(arg)
+ if @type.nil?
+ self.class.new(arg)
+ else
+ arg.call(
+ Application.new(
+ function: Variable.new(name: "Optional"),
+ arguments: [@type]
+ ),
+ Function.new(
+ var: "_",
+ type: @type,
+ body: Optional.new(
+ value: Variable.new(name: "_"),
+ type: @type
+ )
+ ),
+ OptionalNone.new(type: @type)
+ )
+ end
+ end
+
end
class Optional_fold < Builtin
+ def initialize(type=nil, optional=nil, ztype=nil, f=nil)
+ @type = type
+ @optional = optional
+ @ztype = ztype
+ @f = f
+ end
+
+ def call(arg)
+ if @type.nil?
+ self.class.new(arg)
+ elsif arg.is_a?(Optional)
+ self.class.new(@type, arg)
+ elsif !@optional.nil? && @ztype.nil?
+ self.class.new(@type, @optional, arg)
+ elsif !@optional.nil? && @f.nil?
+ self.class.new(@type, @optional, @ztype, arg)
+ elsif !@optional.nil?
+ @optional.reduce(arg, &@f)
+ else
+ super
+ end
+ end
end
class Text_show < Builtin
M lib/dhall/normalize.rb => lib/dhall/normalize.rb +20 -2
@@ 12,6 12,10 @@ module Dhall
map_subexpressions { |expr| expr.shift(amount, name, min_index) }
end
+ def substitute(var, with_expr)
+ map_subexpressions { |expr| expr.substitute(var, with_expr) }
+ end
+
def fusion(*); end
end
@@ 21,7 25,8 @@ module Dhall
normalized = super
return normalized.fuse if normalized.fuse
- if normalized.function.is_a?(Builtin)
+ if normalized.function.is_a?(Builtin) ||
+ normalized.function.is_a?(Function)
return normalized.function.call(*normalized.arguments)
end
@@ 46,6 51,15 @@ module Dhall
body: body.shift(amount, name, min_index + 1)
)
end
+
+ def substitute(svar, with_expr)
+ with_expr = with_expr.shift(1, var, 0)
+ if var == svar.name
+ super(svar.with(index: svar.index + 1), with_expr)
+ else
+ super(svar, with_expr)
+ end
+ end
end
class Forall; end
@@ 58,6 72,10 @@ module Dhall
return self if self.name != name || min_index > index
with(index: index + amount)
end
+
+ def substitute(var, with_expr)
+ self == var ? with_expr : self
+ end
end
class Operator
@@ 293,7 311,7 @@ module Dhall
end
def desugar
- lets.reduce(body) { |inside, let|
+ lets.reverse.reduce(body) { |inside, let|
Application.new(
function: Function.new(
var: let.var,
A test/normalization/beta/FunctionApplicationCaptureA.dhallb => test/normalization/beta/FunctionApplicationCaptureA.dhallb +0 -0
A test/normalization/beta/FunctionApplicationCaptureB.dhallb => test/normalization/beta/FunctionApplicationCaptureB.dhallb +1 -0
@@ 0,0 1,1 @@
+�e5.0.0ax<
\ No newline at end of file
A test/normalization/beta/FunctionApplicationNoSubstituteA.dhallb => test/normalization/beta/FunctionApplicationNoSubstituteA.dhallb +0 -0
A test/normalization/beta/FunctionApplicationNoSubstituteB.dhallb => test/normalization/beta/FunctionApplicationNoSubstituteB.dhallb +1 -0
@@ 0,0 1,1 @@
+�e5.0.0ay<
\ No newline at end of file
A test/normalization/beta/FunctionApplicationNormalizeArgumentsA.dhallb => test/normalization/beta/FunctionApplicationNormalizeArgumentsA.dhallb +0 -0
A test/normalization/beta/FunctionApplicationNormalizeArgumentsB.dhallb => test/normalization/beta/FunctionApplicationNormalizeArgumentsB.dhallb +0 -0
A test/normalization/beta/FunctionApplicationSubstituteA.dhallb => test/normalization/beta/FunctionApplicationSubstituteA.dhallb +0 -0
A test/normalization/beta/FunctionApplicationSubstituteB.dhallb => test/normalization/beta/FunctionApplicationSubstituteB.dhallb +1 -0
@@ 0,0 1,1 @@
+�e5.0.0ay<
\ No newline at end of file
A test/normalization/beta/FunctionNormalizeArgumentsA.dhallb => test/normalization/beta/FunctionNormalizeArgumentsA.dhallb +1 -0
@@ 0,0 1,1 @@
+�e5.0.0�ax��aXaY��axay<
\ No newline at end of file
A test/normalization/beta/FunctionNormalizeArgumentsB.dhallb => test/normalization/beta/FunctionNormalizeArgumentsB.dhallb +1 -0
@@ 0,0 1,1 @@
+�e5.0.0�axaXax<
\ No newline at end of file
A test/normalization/beta/FunctionTypeNormalizeArgumentsA.dhallb => test/normalization/beta/FunctionTypeNormalizeArgumentsA.dhallb +1 -0
@@ 0,0 1,1 @@
+�e5.0.0�ax��aXaY��aAaB<
\ No newline at end of file
A test/normalization/beta/FunctionTypeNormalizeArgumentsB.dhallb => test/normalization/beta/FunctionTypeNormalizeArgumentsB.dhallb +1 -0
@@ 0,0 1,1 @@
+�e5.0.0�axaXaA<
\ No newline at end of file
M test/test_normalization.rb => test/test_normalization.rb +30 -0
@@ 78,4 78,34 @@ class TestParser < Minitest::Test
).shift(1, "x", 0)
)
end
+
+ def test_substitute_variable
+ assert_equal(
+ Dhall::Natural.new(value: 1),
+ Dhall::Variable.new(name: "x", index: 0).substitute(
+ Dhall::Variable.new(name: "x", index: 0),
+ Dhall::Natural.new(value: 1)
+ )
+ )
+ end
+
+ def test_substitute_variable_different_name
+ assert_equal(
+ Dhall::Variable.new(name: "y", index: 0),
+ Dhall::Variable.new(name: "y", index: 0).substitute(
+ Dhall::Variable.new(name: "x", index: 0),
+ Dhall::Natural.new(value: 1)
+ )
+ )
+ end
+
+ def test_substitute_variable_different_index
+ assert_equal(
+ Dhall::Variable.new(name: "x", index: 1),
+ Dhall::Variable.new(name: "x", index: 1).substitute(
+ Dhall::Variable.new(name: "x", index: 0),
+ Dhall::Natural.new(value: 1)
+ )
+ )
+ end
end