diff --git a/Dhallish.gemspec b/Dhallish.gemspec index 5d1a1d561afb6ac4aa184a4b4662d15bbf052076..a07667fada785fd3eb4639d624d603d8d0fd6beb 100644 --- a/Dhallish.gemspec +++ b/Dhallish.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'dhallish' - s.version = '0.0.1' + s.version = '0.2.0' s.licenses = ['MIT'] s.summary = "A Ruby Implementation of a Dhall-like Language" s.description = s.summary diff --git a/README.md b/README.md index 92f7375c3698540c2dbc58c992ee2303ec5269ea..2feca438f08b06d26952f9e10026041ebff7b22b 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ cat ./tests/examples.dhallish ### Missing Features: - `''strings that start with two quotes''` - `Kind` and `Sort` (What are those?) -- `missing` for imports +- `missing` and integrity-hashes for imports - `merge` for empty Unions ### Known Bugs: diff --git a/lib/DhallishGrammar.treetop b/lib/DhallishGrammar.treetop index 15bd77efdd16a4c11331d40764afec793db3dc58..6fecde6263c71b4a9b3cba7755f04f864077f364 100644 --- a/lib/DhallishGrammar.treetop +++ b/lib/DhallishGrammar.treetop @@ -168,7 +168,7 @@ grammar DhallishGrammar ## Union Literals rule union_literal - "<" 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? ">" + "<" space? start:(lb:label space? ":" space? type:record_merge_expression space? "|" space?)* lb:label space? "=" space? expr:add_sub_expression tail:(space? "|" space? lb:label space? ":" space? type:record_merge_expression)* space? ">" { def to_node() typed_labels = {} @@ -404,6 +404,7 @@ grammar DhallishGrammar exp:optional_literal { def to_node() exp.to_node() end } / exp:forall_literal { def to_node() exp.to_node() end } / exp:import_expression { def to_node() exp.to_node() end } / + exp:union_merge { def to_node() exp.to_node() end } / exp:label { def to_node() exp.to_node() end } / exp:record_literal { def to_node() exp.to_node() end } / exp:record_type_literal { def to_node() exp.to_node() end } / @@ -485,4 +486,13 @@ grammar DhallishGrammar (!([\s] / "\\" / "<" / ">" / "?" / "," / "[" / "]" / "{" / "}" / "#" / "(" / ")") .)* end + rule union_merge + "merge" space? handler:primitive_expression space? union:primitive_expression + { + def to_node() + Dhallish::Ast::UnionMerge.new(handler.to_node(), union.to_node()) + end + } + end + end diff --git a/lib/ast.rb b/lib/ast.rb index 2298cb8b42ea1f2bfca9b757d58b2bcf94065ff1..e4a5e354a8fb69c41267f117fa776e844f74dedd 100644 --- a/lib/ast.rb +++ b/lib/ast.rb @@ -702,8 +702,7 @@ module Dhallish end def evaluate(ctx) - # TODO: is there a evaluate missing? - Union.new @init_label, @init_val, @type + Union.new @init_label, @init_val.evaluate(ctx), @type end end @@ -751,16 +750,16 @@ module Dhallish hdlr.is_a? Types::Record and unin.is_a? Types::Union and hdlr.types.size == unin.types.size and hdlr.types.size > 0 } rettype = nil - hdlr.types.each { |key, type| - fn = unin.types[key] - assert("`merge` record (of functions) and union must have the same fields") { !fn.nil? and fn.is_a? Types::Function } + hdlr.types.each { |key, fntype| + argtype = unin.types[key] + assert("`merge` record (of functions) and union must have the same fields") { !argtype.nil? and fntype.is_a? Types::Function } if rettype.nil? - rettype = fn.restype + rettype = fntype.restype else # TODO: unification instead of == - assert("`merge` functions must all have the same return type") { fn.restype == rettype } + assert("`merge` functions must all have the same return type") { fntype.restype == rettype } end - assert("`merge` functions must take as argument the type of its union field") { fn.argtype } + assert("`merge` functions must take as argument the type of its union field") { fntype.argtype == argtype } } rettype end diff --git a/lib/stdlib.rb b/lib/stdlib.rb index aec7947aff9b08a0dd221ce24ac61a9224a9b369..48a05057b39ffb91fc4f8d1a62306066559471f1 100644 --- a/lib/stdlib.rb +++ b/lib/stdlib.rb @@ -48,6 +48,9 @@ module Dhallish globalctx["Double/show"] = BuiltinFunction.new { |arg| arg.to_s } types["Double/show"] = Types::Function.new(Types::Double, Types::Text) + globalctx["Text/show"] = BuiltinFunction.new { |arg| arg } + types["Text/show"] = Types::Function.new(Types::Text, Types::Text) + # Casts: natToInt = BuiltinFunction.new { |arg| arg } globalctx["Natural/toInteger"] = natToInt diff --git a/tests/test.rb b/tests/test.rb index bbeb9dc7d215c74dba9131e11a4e69fe59924640..6e1205583dde98b1117477db603d1e7d3026867d 100755 --- a/tests/test.rb +++ b/tests/test.rb @@ -35,7 +35,8 @@ positive_tests = { "function with two type parameters": ["let f = \\(t : Type) -> \\(u : Type) -> \\(x : t) -> \\(y : u) -> x in (f : forall(t : Type) -> forall(u : Type) -> t -> u -> t) Natural Integer 1 -1 ", 1 ], "fallback for file import": ["let x = ./foobarfoobar.dhall as Text ? 1 in x", 1], "more type annotation": [ "let f = \\(a: Type) -> \\(l: List a) -> \\(x: a) -> x in (f: forall(b: Type) -> forall(l: List b) -> b -> b) Natural [1, 2, 3] 5", 5 ], - "select from union": [ "let union = <a : Natural | b : Text>.a 17 in union.a : Optional Natural", 17 ] + "select from union": [ "let union = <a : Natural | b : Text>.a 17 in union.a : Optional Natural", 17 ], + "union merge": [ "merge { Left = \\(x: Natural) -> Natural/show x, Right = \\(y: Integer) -> Integer/show y } < Left = 42 | Right : Integer >", "42" ] } negative_tests = {