diff --git a/lib/DhallishGrammar.treetop b/lib/DhallishGrammar.treetop index f6e9c9bb99118b754d0c8e6f87e2f102171d6252..703588a01b816e47ff7a511122fc222d2252a243 100644 --- a/lib/DhallishGrammar.treetop +++ b/lib/DhallishGrammar.treetop @@ -167,12 +167,8 @@ grammar DhallishGrammar end ## Union Literals - rule union_literal_one_member - "<" space? label space? "=" space? expression ">" - end - rule union_literal - "<" space? start:(lb:label space? ":" space? type:expression space? "|" space?)* lb:label space? "=" space? expr:expression tail:(space? "|" space? lb:label space? ":" space? type:expression)* space? ">" + "<" space? start:(lb:label space? ":" space? type:record_merge_expression space? "|" space?)* lb:label space? "=" space? expr:record_merge_expression tail:(space? "|" space? lb:label space? ":" space? type:record_merge_expression)* space? ">" { def to_node(ctx) typed_labels = {} @@ -193,7 +189,7 @@ grammar DhallishGrammar rule union_type_literal empty_union_type_literal { def to_node(ctx) Dhallish::Ast::UnionType.new({}) end } / - "<" space? lb:label space? ":" space? type:expression tail:(space? "|" space? lb:label space? ":" space? type:expression)* space? ">" + "<" space? lb:label space? ":" space? type:record_merge_expression tail:(space? "|" space? lb:label space? ":" space? type:record_merge_expression)* space? ">" { def to_node(ctx) type_map = {lb.text_value => type.to_node(ctx)} @@ -388,7 +384,7 @@ grammar DhallishGrammar def to_node(ctx) tail.elements.reduce(pe.to_node(ctx)) { |tree, node| if node.sel.respond_to? :a - Dhallish::Ast::RecordSelector.new tree, node.sel.a.text_value + Dhallish::Ast::RecordUnionSelector.new tree, node.sel.a.text_value else Dhallish::Ast::RecordProjection.new tree, node.sel.b.to_list() end diff --git a/lib/ast.rb b/lib/ast.rb index d45d57cb86845a29f807e07107834daca2d333df..1e1a59ba4b919f691c86407be96a1c4d861511be 100644 --- a/lib/ast.rb +++ b/lib/ast.rb @@ -422,7 +422,7 @@ module Dhallish end # rec: ast node of something that will be a record, key: name of key to access in record (string) - class RecordSelector + class RecordUnionSelector attr_accessor :rec attr_accessor :key def initialize(rec, key) @@ -432,13 +432,29 @@ module Dhallish def compute_type(ctx) rectype = @rec.compute_type ctx - assert("`.` can only be used on records, the key `#{@key}` must exist") { rectype.is_a? Types::Record and !rectype.types[@key].nil? } - rectype.types[@key] + assert("`.` can only be used on records and unions, the key `#{@key}` must exist") { + ((rectype.is_a? Types::Record or rectype.is_a? Types::Union) and !rectype.types[@key].nil?) \ + or (rectype.is_a? Types::Type and rectype.metadata.is_a? Types::Union) + } + if rectype.is_a? Types::Union + Types::Optional.new rectype.types[@key] + elsif rectype.is_a? Types::Record + rectype.types[@key] + else + union_type = rectype.metadata + Types::Function.new union_type.types[@key], union_type + end end def evaluate(ctx) rec = @rec.evaluate ctx - rec[@key] + if rec.is_a? Union + rec.select @key + elsif rec.is_a? Hash # <== Record + rec[@key] + else + BuiltinFunction.new { |val| Union.new @key, val, rec } + end end end @@ -662,6 +678,8 @@ module Dhallish attr_accessor :map attr_accessor :init_label attr_accessor :init_val + attr_accessor :type + def initialize(map, init_label, init_val) @map = map @init_label = init_label @@ -670,18 +688,21 @@ module Dhallish def compute_type(ctx) types = {} - @map.each { |key, node| + @map.keys.each { |key| + node = @map[key] assert ("duplicate labels not allowed in union literals") { !types.include? key } - types[key] = node.compute_type(ctx) + type_type = node.compute_type(ctx) + assert ("Type annotation in Union Literal not a type") { type_type.is_a? Types::Type } + @map[key] = types[key] = type_type.metadata } assert ("duplicate labels not allowed in union literals") { !types.include? @init_label } types[@init_label] = @init_val.compute_type(ctx) - Types::Union.new(types) + @type = Types::Union.new(types) + @type end def evaluate(ctx) - res = {@init_label => @init_val.evaluate(ctx)} - res + Union.new @init_label, @init_val, @type end end