From d5ff7d89be76f8f785a0e415dfe07ec868a33681 Mon Sep 17 00:00:00 2001 From: Lou Knauer <lou.knauer@gmx.de> Date: Tue, 5 Mar 2019 12:01:45 +0100 Subject: [PATCH] removed Value class and adapted stdlib --- lib/stdlib.rb | 177 ++++++++---------- lib/types.rb | 111 +++-------- lib/utils.rb | 4 + examples.dhallish => tests/examples.dhallish | 9 +- .../examples_expected.json | 2 +- tests/test.rb | 4 +- 6 files changed, 121 insertions(+), 186 deletions(-) rename examples.dhallish => tests/examples.dhallish (92%) rename examples.expected.json => tests/examples_expected.json (67%) diff --git a/lib/stdlib.rb b/lib/stdlib.rb index 02501b8..edbab7a 100644 --- a/lib/stdlib.rb +++ b/lib/stdlib.rb @@ -22,12 +22,12 @@ module Dhallish def fill_context(globalctx, types) # Types: - globalctx["Natural"] = Value.new(Types::Natural, Types::Type.new) - globalctx["Integer"] = Value.new(Types::Integer, Types::Type.new) - globalctx["Double"] = Value.new(Types::Double, Types::Type.new) - globalctx["Bool"] = Value.new(Types::Bool, Types::Type.new) - globalctx["Text"] = Value.new(Types::Text, Types::Type.new) - globalctx["Type"] = Value.new(Types::Type.new, Types::Type.new) + globalctx["Natural"] = Types::Natural + globalctx["Integer"] = Types::Integer + globalctx["Double"] = Types::Double + globalctx["Bool"] = Types::Bool + globalctx["Text"] = Types::Text + globalctx["Type"] = Types::Type.new types["Natural"] = Types::Type.new(Types::Natural) types["Integer"] = Types::Type.new(Types::Integer) @@ -37,58 +37,53 @@ module Dhallish types["Type"] = Types::Type.new(Types::Type.new) # Shows: - natShow = BuiltinFunction.new { |arg| Value.new arg.val.to_s, Types::Text } - globalctx["Natural/show"] = Value.new(natShow, Types::Function.new(Types::Natural, Types::Text)) + globalctx["Natural/show"] = BuiltinFunction.new { |arg| arg.to_s } types["Natural/show"] = Types::Function.new(Types::Natural, Types::Text) - intShow = BuiltinFunction.new { |arg| Value.new arg.val.to_s, Types::Text } - globalctx["Integer/show"] = Value.new(intShow, Types::Function.new(Types::Integer, Types::Text)) + globalctx["Integer/show"] = BuiltinFunction.new { |arg| arg.to_s } types["Integer/show"] = Types::Function.new(Types::Integer, Types::Text) - doubleShow = BuiltinFunction.new { |arg| Value.new arg.val.to_s, Types::Text } - globalctx["Double/show"] = Value.new(doubleShow, Types::Function.new(Types::Double, Types::Text)) + globalctx["Double/show"] = BuiltinFunction.new { |arg| arg.to_s } types["Double/show"] = Types::Function.new(Types::Double, Types::Text) # Casts: - natToInt = BuiltinFunction.new { |arg| Value.new arg.val, Types::Integer } - globalctx["Natural/toInteger"] = Value.new(natToInt, Types::Function.new(Types::Natural, Types::Integer)) + natToInt = BuiltinFunction.new { |arg| arg } + globalctx["Natural/toInteger"] = natToInt types["Natural/toInteger"] = Types::Function.new(Types::Natural, Types::Integer) - natToDou = BuiltinFunction.new { |arg| Value.new arg.val.to_f, Types::Double } - globalctx["Natural/toDouble"] = Value.new(natToDou, Types::Function.new(Types::Natural, Types::Double)) + natToDou = BuiltinFunction.new { |arg| arg.to_f } + globalctx["Natural/toDouble"] = natToDou types["Natural/toDouble"] = Types::Function.new(Types::Natural, Types::Double) - intToNat = BuiltinFunction.new { |arg| Value.new arg.val.abs, Types::Natural } - globalctx["Integer/toNatural"] = Value.new(intToNat, Types::Function.new(Types::Integer, Types::Natural)) + intToNat = BuiltinFunction.new { |arg| arg.abs } + globalctx["Integer/toNatural"] = intToNat types["Integer/toNatural"] = Types::Function.new(Types::Integer, Types::Natural) - intToDou = BuiltinFunction.new { |arg| Value.new arg.val.to_f, Types::Double } - globalctx["Integer/toDouble"] = Value.new(intToDou, Types::Function.new(Types::Integer, Types::Double)) + intToDou = BuiltinFunction.new { |arg| arg.to_f } + globalctx["Integer/toDouble"] = intToDou types["Integer/toDouble"] = Types::Function.new(Types::Integer, Types::Double) - douToNat = BuiltinFunction.new { |arg| Value.new arg.val.round.abs, Types::Natural } - globalctx["Double/toNatural"] = Value.new(douToNat, Types::Function.new(Types::Double, Types::Natural)) + douToNat = BuiltinFunction.new { |arg| arg.round.abs } + globalctx["Double/toNatural"] = douToNat types["Double/toNatural"] = Types::Function.new(Types::Double, Types::Natural) - douToInt = BuiltinFunction.new { |arg| Value.new arg.val.round, Types::Integer } - globalctx["Double/toInteger"] = Value.new(douToInt, Types::Function.new(Types::Double, Types::Integer)) + douToInt = BuiltinFunction.new { |arg| arg.round } + globalctx["Double/toInteger"] = douToInt types["Double/toInteger"] = Types::Function.new(Types::Double, Types::Integer) # Lists: - globalctx["List"] = Value.new(BuiltinFunction.new { |a| - Value.new(Types::List.new(a.val), Types::Type.new) - }, make_fn_type([Types::Type.new, :a], Types::Type.new(Types::List.new(Types::Unresolved.new(:a))))) + globalctx["List"] = BuiltinFunction.new { |a| Types::List.new(a) } types["List"] = make_fn_type([Types::Type.new, :a], Types::Type.new(Types::List.new(Types::Unresolved.new(:a)))) list_length_type = make_fn_type([Types::Type.new(Types::Unresolved.new(:a)), :a], Types::List.new(Types::Unresolved.new(:a)), Types::Natural) - globalctx["List/length"] = Value.new(BuiltinFunction.new{ |a| - Value.new(BuiltinFunction.new { |list| - Value.new(list.val.length, Types::Natural) - }, Types::resolve(list_length_type.restype, :a, a.val)) - }, list_length_type) + globalctx["List/length"] = BuiltinFunction.new{ |a| + BuiltinFunction.new { |list| + list.length + } + } types["List/length"] = list_length_type list_fold_type = make_fn_type( @@ -97,67 +92,60 @@ module Dhallish [Types::Type.new(Types::Unresolved.new(:b)), :b], make_fn_type(:a, :b, :b), :b, :b) - globalctx["List/fold"] = Value.new(BuiltinFunction.new { |a| - type = Types::resolve(list_fold_type.restype, :a, a.val) - Value.new(BuiltinFunction.new { |list| - type = type.restype - Value.new(BuiltinFunction.new { |b| - type = Types::resolve(type.restype, :b, b.val) - Value.new(BuiltinFunction.new { |f| - type = type.restype - Value.new(BuiltinFunction.new { |x| - list.val.reduce(x) { |acc, x| f.call(x).call(acc) } - }, type) - }, type) - }, type) - }, type) - }, list_fold_type) + globalctx["List/fold"] = BuiltinFunction.new { |a| + BuiltinFunction.new { |list| + BuiltinFunction.new { |b| + BuiltinFunction.new { |f| + BuiltinFunction.new { |x| + list.reduce(x) { |acc, x| f.call(x).call(acc) } + } + } + } + } + } types["List/fold"] = list_fold_type list_head_type = make_fn_type( [Types::Type.new(Types::Unresolved.new(:a)), :a], Types::List.new(Types::Unresolved.new(:a)), Types::Optional.new(Types::Unresolved.new(:a))) - globalctx["List/head"] = Value.new(BuiltinFunction.new { |a| - type = Types::resolve(list_head_type.restype, :a, a.val) - Value.new(BuiltinFunction.new { |list| - Value.new(list.val.first, type.restype) - }, type) - }, list_head_type) + globalctx["List/head"] = BuiltinFunction.new { |a| + BuiltinFunction.new { |list| + list.first + } + } types["List/head"] = list_head_type list_last_type = make_fn_type( [Types::Type.new(Types::Unresolved.new(:a)), :a], Types::List.new(Types::Unresolved.new(:a)), Types::Optional.new(Types::Unresolved.new(:a))) - globalctx["List/last"] = Value.new(BuiltinFunction.new { |a| - type = Types::resolve(list_last_type.restype, :a, a.val) - Value.new(BuiltinFunction.new { |list| - Value.new(list.val.last, type.restype) - }, type) - }, list_last_type) + globalctx["List/last"] = BuiltinFunction.new { |a| + BuiltinFunction.new { |list| + list.last + } + } types["List/last"] = list_last_type list_tail_type = make_fn_type( [Types::Type.new(Types::Unresolved.new(:a)), :a], Types::List.new(Types::Unresolved.new(:a)), Types::List.new(Types::Unresolved.new(:a))) - globalctx["List/tail"] = Value.new(BuiltinFunction.new { |a| - type = Types::resolve(list_tail_type.restype, :a, a.val) - Value.new(BuiltinFunction.new { |list| - if list.val.empty? - Value.new([], type.restype) + globalctx["List/tail"] = BuiltinFunction.new { |a| + BuiltinFunction.new { |list| + if list.empty? + [] else - Value.new(list.val[1..list.val.length], type.restype) + list[1..list.length] end - }, type) - }, list_tail_type) + } + } types["List/tail"] = list_tail_type # Optionals: - globalctx["Optional"] = Value.new(BuiltinFunction.new { |a| - Value.new(Types::Optional.new(a.val), Types::Type.new) - }, make_fn_type([Types::Type.new, :a], Types::Type.new(Types::Optional.new(Types::Unresolved.new(:a))))) + globalctx["Optional"] = BuiltinFunction.new { |a| + Types::Optional.new(a) + } types["Optional"] = make_fn_type([Types::Type.new, :a], Types::Type.new(Types::Optional.new(Types::Unresolved.new(:a)))) optional_fold_type = make_fn_type( @@ -166,25 +154,21 @@ module Dhallish [Types::Type.new(Types::Unresolved.new(:b)), :b], make_fn_type(:a, :b), :b, :b) - globalctx["Optional/fold"] = Value.new(BuiltinFunction.new { |a| - type = Types::resolve(optional_fold_type.restype, :a, a.val) - Value.new(BuiltinFunction.new { |opt| - type = type.restype - Value.new(BuiltinFunction.new { |b| - type = Types::resolve(type.restype, :b, b.val) - Value.new(BuiltinFunction.new { |f| - type = type.restype - Value.new(BuiltinFunction.new { |x| - if opt.val.nil? + globalctx["Optional/fold"] = BuiltinFunction.new { |a| + BuiltinFunction.new { |opt| + BuiltinFunction.new { |b| + BuiltinFunction.new { |f| + BuiltinFunction.new { |x| + if opt.nil? x else - f.call(opt.val) + f.call(opt) end - }, type) - }, type) - }, type) - }, type) - }, optional_fold_type) + } + } + } + } + } types["Optional/fold"] = optional_fold_type # Naturals: @@ -193,20 +177,17 @@ module Dhallish [Types::Type.new(Types::Unresolved.new(:a)), :a], make_fn_type(:a, :a), :a, :a) - globalctx["Natural/fold"] = Value.new(BuiltinFunction.new { |n| - type = natural_fold_type.restype - Value.new(BuiltinFunction.new { |a| - type = Types::resolve(type.restype, :a, a.val) - Value.new(BuiltinFunction.new { |succ| - type = type.restype - Value.new(BuiltinFunction.new { |zero| + globalctx["Natural/fold"] = BuiltinFunction.new { |n| + BuiltinFunction.new { |a| + BuiltinFunction.new { |succ| + BuiltinFunction.new { |zero| res = zero - n.val.times { res = succ.call(res) } + n.times { res = succ.call(res) } res - }, type) - }, type) - }, type) - }, natural_fold_type) + } + } + } + } types["Natural/fold"] = natural_fold_type end diff --git a/lib/types.rb b/lib/types.rb index fe558f0..ae766cb 100644 --- a/lib/types.rb +++ b/lib/types.rb @@ -138,7 +138,7 @@ module Dhallish argtype = orgtype.argtype if orgtype.unres.nil? or orgtype.unres != name restype = resolve(orgtype.restype, name, newtype) - argtype = resolve(orgtype.argtype, name, newtype) + argtype = resolve(orgtype.argtype, name, newtype) end Function.new(argtype, restype, orgtype.unres) end @@ -241,88 +241,27 @@ module Dhallish Numbers = [Natural, Integer, Double] end - # Can be used as AST-Node. - class Value - attr_accessor :val - attr_accessor :type - def initialize(val, type) - @val = val - @type = type - end - - def compute_type(ctx) - return @type - end - - def to_json() - case type - when Types::Natural - val.to_s - when Types::Integer - val.to_s - when Types::Double - val.to_s - when Types::Bool - if val - "true" - else - "false" - end - when Types::Text - "\"#{val.gsub "\n", "\\n"}\"" - when Types::Optional - if val.nil? - "null" - else - val.to_json - end - when Types::List - "[ #{val.map{ |elm| - elm.to_json - }.join(", ")} ]" - when Types::Record - elms = [] - val.each { |key, val| - elms.push("\"#{key}\": #{val.to_json}") - } - "{ #{elms.join ", "} }" - else - "{ \"type\": \"#{@type.to_s}\", \"value\": \"#{@val.to_s}\" }" - end - end - - def evaluate(ctx) self end - - def call(arg) - assert("not a function: #{@type}") { @type.is_a? Types::Function } - assert("argument type missmatch: #{arg.type} vs. #{@type.argtype}") { arg.type == @type.argtype } - - res = nil - if @val.is_a? BuiltinFunction - res = @val.block.call arg - elsif @val.is_a? Function - ctx = Context.new(@val.ctx) - ctx[@val.argname] = arg - res = @val.ast.evaluate ctx - if res.type.is_a? Types::Function and !@type.restype.nil? - res.type.restype = @type.restype.restype - res.type.unres = @type.restype.unres - end - end - - if !@type.restype.nil? - if !@type.unres.nil? - expected_type = Types::resolve(@type.restype, @type.unres, arg.val) - res.type = Types::resolve(res.type, @type.unres, arg.val) - assert ("return type mismatch. expected #{expected_type}, actual #{res.type}") { res.type == expected_type } - else - assert("function return type missmatch: actual type #{res.type}, expected #{@type.restype}") { res.type == @type.restype } - end - end - - res + def to_json(dhallval) + case dhallval + when Integer + dhallval.to_s + when Float + dhallval.to_s + when String + "\"#{escape_str(dhallval)}\"" + when TrueClass + "true" + when FalseClass + "false" + when Array + "[#{ dhallval.map{ |val| to_json(val) }.join(", ") }]" + when Hash + "{#{ dhallval.map{ |key, val| "\"#{escape_str(key)}\": #{ to_json(val) }" }.join(", ") }}" + else + "\"Dhallish-Internal: #{escape_str(dhallval.inspect)}\"" end end + module_function :to_json # To be used as Dhallish::Value.val for dhall-defined functions class Function @@ -334,6 +273,12 @@ module Dhallish @ast = ast @ctx = ctx end + + def call(arg) + newctx = Context.new ctx + newctx[@argname] = arg + @ast.evaluate newctx + end end # To be used as Dhallish::Value.val for ruby-defined functions @@ -342,6 +287,8 @@ module Dhallish def initialize(&block) @block = block end + + def call(arg) @block.call arg end end class Context @@ -384,6 +331,7 @@ module Dhallish end module_function :mergeRecordTypes + # TODO: .val weg! def mergeRecordsRecursively(a, b) restype = mergeRecordTypes(a.type, b.type) @@ -412,6 +360,7 @@ module Dhallish end module_function :mergeRecordTypesPrefereRight + # TODO: .val weg! def mergeRecordsPrefereRight(a, b) mergedType = mergeRecordTypesPrefereRight(a.type, b.type) mergedVals = a.val.clone diff --git a/lib/utils.rb b/lib/utils.rb index 4670dc3..bb05b08 100644 --- a/lib/utils.rb +++ b/lib/utils.rb @@ -18,3 +18,7 @@ def get_new_sym() $dhallish_internal_global_counter += 1 ("__newsym_#{$dhallish_internal_global_counter}").to_sym end + +def escape_str(str) + str.gsub("\n", "\\n").gsub("\"", "\\\"") +end diff --git a/examples.dhallish b/tests/examples.dhallish similarity index 92% rename from examples.dhallish rename to tests/examples.dhallish index 8a50c39..db3b9f1 100644 --- a/examples.dhallish +++ b/tests/examples.dhallish @@ -36,15 +36,16 @@ let min = \(x: Natural) -> \(y: Natural) -> if x < y then x else y let List/atIndex = \(a: Type) -> \(list: List a) -> \(idx: Natural) -> List/head a (Natural/fold idx (List a) (\(l: List a) -> List/tail a l) list) +let Pair = \(a: Type) -> \(b: Type) -> { x: a, y: b } + let List/zip = \(a: Type) -> \(b: Type) -> \(la: List a) -> \(lb: List b) -> \(dummyA: a) -> \(dummyB: b) -> let idxList = genList(min (List/length a la) (List/length b lb)) - let recType = { first: a, second: b } - let zipper = \(idx: Natural) -> \(list: List recType) -> + let zipper = \(idx: Natural) -> \(list: List (Pair a b)) -> let first = Optional/getOrElse a (List/atIndex a la idx) dummyA let second = Optional/getOrElse b (List/atIndex b lb idx) dummyB - in list # [{ first = first, second = second }] + in list # [{ x = first, y = second }] in - List/fold Natural idxList (List recType) zipper ([]: List recType) + List/fold Natural idxList (List (Pair a b)) zipper ([]: List (Pair a b)) in { math = ( diff --git a/examples.expected.json b/tests/examples_expected.json similarity index 67% rename from examples.expected.json rename to tests/examples_expected.json index b6eaab0..c874d2a 100644 --- a/examples.expected.json +++ b/tests/examples_expected.json @@ -9,5 +9,5 @@ { "a": 3, "b": 9 }, { "a": 4, "b": 16 } ], "fibonacci": [ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ], - "zip": [ { "first": 1, "second": "one" }, { "first": 2, "second": "two" }, { "first": 3, "second": "three" }, { "first": 4, "second": "four" } ] + "zip": [ { "x": 1, "y": "one" }, { "x": 2, "y": "two" }, { "x": 3, "y": "three" }, { "x": 4, "y": "four" } ] } diff --git a/tests/test.rb b/tests/test.rb index 6af6663..47f32b1 100755 --- a/tests/test.rb +++ b/tests/test.rb @@ -33,7 +33,8 @@ positive_tests = { "simple function type annotation": [ "(Natural/show : Natural -> Text) 2", "2" ], "identity with type parameter": ["let f = \\(t : Type) -> \\(x : t) -> x in (f : forall(t : Type) -> t -> t) Natural 2", 2], "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] + "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 ] } negative_tests = { @@ -128,4 +129,3 @@ dhallfiles.each { |file| end end } - -- GitLab