M lib/dhall/ast.rb => lib/dhall/ast.rb +11 -0
@@ 810,6 810,17 @@ module Dhall
end
end
+ class RecordProjectionByExpression < Expression
+ include(ValueSemantics.for_attributes do
+ record Expression
+ selector Expression
+ end)
+
+ def as_json
+ [10, record.as_json, [selector.as_json]]
+ end
+ end
+
class EmptyRecordProjection < Expression
include(ValueSemantics.for_attributes do
record Expression
M lib/dhall/binary.rb => lib/dhall/binary.rb +9 -1
@@ 144,7 144,15 @@ module Dhall
class RecordProjection
def self.decode(record, *selectors)
- self.for(Dhall.decode(record), selectors)
+ record = Dhall.decode(record)
+ if selectors.length == 1 && selectors[0].is_a?(Array)
+ RecordProjectionByExpression.new(
+ record: record,
+ selector: Dhall.decode(selectors[0][0])
+ )
+ else
+ self.for(record, selectors)
+ end
end
end
M lib/dhall/normalize.rb => lib/dhall/normalize.rb +12 -0
@@ 320,6 320,18 @@ module Dhall
end
end
+ class RecordProjectionByExpression
+ def normalize
+ sel = selector.normalize
+
+ if sel.is_a?(RecordType)
+ RecordProjection.for(record, sel.keys).normalize
+ else
+ with(record: record.normalize, selector: sel)
+ end
+ end
+ end
+
class EmptyRecordProjection
def normalize
EmptyRecord.new
M lib/dhall/parser.rb => lib/dhall/parser.rb +2 -0
@@ 146,6 146,8 @@ module Dhall
selectors.reduce(record) do |rec, sels|
if sels.is_a?(Array)
RecordProjection.for(rec, sels)
+ elsif sels.is_a?(Dhall::Expression)
+ RecordProjectionByExpression.new(record: rec, selector: sels)
else
RecordSelection.new(record: rec, selector: sels)
end
M lib/dhall/typecheck.rb => lib/dhall/typecheck.rb +28 -0
@@ 604,6 604,34 @@ module Dhall
end
end
+ class RecordProjectionByExpression
+ TypeChecker.register self, Dhall::RecordProjectionByExpression
+
+ def initialize(projection)
+ @selector = projection.selector.normalize
+ @projection = projection
+ end
+
+ def annotate(context)
+ TypeChecker.assert @selector, Dhall::RecordType,
+ "RecordProjectionByExpression on #{@selector.class}"
+
+ projection = Dhall::RecordProjection.for(
+ @projection.record,
+ @selector.keys
+ )
+
+ TypeChecker.assert_type projection, @selector.normalize,
+ "Type doesn't match #{@selector}",
+ context: context
+
+ Dhall::TypeAnnotation.new(
+ value: @projection,
+ type: @selector
+ )
+ end
+ end
+
class Enum
TypeChecker.register self, Dhall::Enum