diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..6f9fbd81e3457d0dda888a32acfed99c8dfd9e10
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "dhall-lang"]
+ path = dhall-lang
+ url = https://github.com/dhall-lang/dhall-lang.git
diff --git a/dhall-lang b/dhall-lang
new file mode 160000
index 0000000000000000000000000000000000000000..b583925f00ef787e201bb07bed0e6fef0db9463d
--- /dev/null
+++ b/dhall-lang
@@ -0,0 +1 @@
+Subproject commit b583925f00ef787e201bb07bed0e6fef0db9463d
diff --git a/lib/ast.rb b/lib/ast.rb
index e4a5e354a8fb69c41267f117fa776e844f74dedd..8c57919cc0025d343e5fe5114c3eaf8b62c4e154 100644
--- a/lib/ast.rb
+++ b/lib/ast.rb
@@ -636,7 +636,7 @@ module Dhallish
new_path = File.dirname src
else
opath = basedir.path
- basedir.path = File.join basedir.path, (@prefix + src)
+ basedir.path = File.join basedir.path, (@prefix + src)
src = basedir.to_s
basedir.path = File.dirname basedir.path
new_path = URI basedir.to_s
@@ -652,6 +652,8 @@ module Dhallish
new_path.path = File.dirname new_path.path
end
+ # STDERR.puts "import src: #{src}"
+
file = open(src)
text = file.read
diff --git a/lib/stdlib.rb b/lib/stdlib.rb
index 48a05057b39ffb91fc4f8d1a62306066559471f1..cd5f24b71aef34e5a3a56d866288eb8697ec99f7 100644
--- a/lib/stdlib.rb
+++ b/lib/stdlib.rb
@@ -21,6 +21,12 @@ module Dhallish
end
module_function :make_fn_type
+ def define(ctx, name, value, type)
+ ctx["<#TYPES#>"][name.to_s] = type
+ ctx["<#VALS#>"][name.to_s] = value
+ end
+ module_function :define
+
def fill_context(globalctx, types)
# Types:
@@ -78,7 +84,7 @@ module Dhallish
globalctx["Double/toInteger"] = douToInt
types["Double/toInteger"] = Types::Function.new(Types::Double, Types::Integer)
-
+=begin
# Lists:
globalctx["List"] = BuiltinFunction.new { |a| Types::List.new(a) }
types["List"] = make_fn_type([:list_a], Types::Type.new(Types::List.new(Types::Unresolved.new(:list_a))))
@@ -262,6 +268,191 @@ module Dhallish
f.call(Types::Natural).call(succ).call(zero)
}
types["Natural/build"] = make_fn_type(make_fn_type([:nat_build_a], make_fn_type(:nat_build_a, :nat_build_a), :nat_build_a, :nat_build_a), Types::Natural)
+=end
+
+ # Lists:
+ globalctx["List"] = BuiltinFunction.new { |a| Types::List.new(a) }
+ types["List"] = make_fn_type([:a], Types::Type.new(Types::List.new(Types::Unresolved.new(:a))))
+
+ list_length_type = make_fn_type([:a], Types::List.new(Types::Unresolved.new(:a)), Types::Natural)
+ globalctx["List/length"] = BuiltinFunction.new{ |a|
+ BuiltinFunction.new { |list|
+ list.length
+ }
+ }
+ types["List/length"] = list_length_type
+
+ list_fold_type = make_fn_type(
+ [:a],
+ Types::List.new(Types::Unresolved.new(:a)),
+ [:b],
+ make_fn_type(:a, :b, :b),
+ :b, :b)
+ globalctx["List/fold"] = BuiltinFunction.new { |a|
+ BuiltinFunction.new { |list|
+ BuiltinFunction.new { |b|
+ BuiltinFunction.new { |f|
+ BuiltinFunction.new { |x|
+ acc = x
+ list.reverse_each { |elm| acc = f.call(elm).call(acc) }
+ acc
+ }
+ }
+ }
+ }
+ }
+ types["List/fold"] = list_fold_type
+
+ list_head_type = make_fn_type(
+ [:a],
+ Types::List.new(Types::Unresolved.new(:a)),
+ Types::Optional.new(Types::Unresolved.new(:a)))
+ globalctx["List/head"] = BuiltinFunction.new { |a|
+ BuiltinFunction.new { |list|
+ list.first
+ }
+ }
+ types["List/head"] = list_head_type
+
+ list_last_type = make_fn_type(
+ [:a],
+ Types::List.new(Types::Unresolved.new(:a)),
+ Types::Optional.new(Types::Unresolved.new(:a)))
+ globalctx["List/last"] = BuiltinFunction.new { |a|
+ BuiltinFunction.new { |list|
+ list.last
+ }
+ }
+ types["List/last"] = list_last_type
+
+ list_tail_type = make_fn_type(
+ [:a],
+ Types::List.new(Types::Unresolved.new(:a)),
+ Types::List.new(Types::Unresolved.new(:a)))
+ globalctx["List/tail"] = BuiltinFunction.new { |a|
+ BuiltinFunction.new { |list|
+ if list.empty?
+ []
+ else
+ list[1..list.length]
+ end
+ }
+ }
+ types["List/tail"] = list_tail_type
+
+ types["List/reverse"] = make_fn_type(
+ [:a],
+ Types::List.new(Types::Unresolved.new(:a)),
+ Types::List.new(Types::Unresolved.new(:a)))
+ globalctx["List/reverse"] = BuiltinFunction.new { |a|
+ BuiltinFunction.new { |list| list.reverse }
+ }
+
+ types["List/build"] = make_fn_type(
+ [:a],
+ make_fn_type(
+ [:b],
+ make_fn_type(:a, :b, :b), :b, :b),
+ Types::List.new(Types::Unresolved.new(:a)))
+ globalctx["List/build"] = BuiltinFunction.new { |a|
+ BuiltinFunction.new { |f|
+ cons = BuiltinFunction.new { |x|
+ BuiltinFunction.new { |list| [x] + list }
+ }
+ f.call(Types::List.new(a)).call(cons).call([])
+ }
+ }
+
+ types["List/indexed"] = make_fn_type(
+ [:a],
+ Types::List.new(Types::Unresolved.new(:a)),
+ Types::List.new(Types::Record.new({
+ "index" => Types::Natural,
+ "value" => Types::Unresolved.new(:a)})))
+ globalctx["List/indexed"] = BuiltinFunction.new { |a|
+ BuiltinFunction.new { |list|
+ list.map.with_index { |val, idx|
+ { "index" => idx, "value" => val }
+ }
+ }
+ }
+
+ # Optionals:
+ globalctx["Optional"] = BuiltinFunction.new { |a|
+ Types::Optional.new(a)
+ }
+ types["Optional"] = make_fn_type([:opt_a], Types::Type.new(Types::Optional.new(Types::Unresolved.new(:opt_a))))
+
+ optional_fold_type = make_fn_type(
+ [:a],
+ Types::Optional.new(Types::Unresolved.new(:a)),
+ [:b],
+ make_fn_type(:a, :b),
+ :b, :b)
+ 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)
+ end
+ }
+ }
+ }
+ }
+ }
+ types["Optional/fold"] = optional_fold_type
+
+ types["Optional/build"] = make_fn_type(
+ [:a],
+ make_fn_type([:b],
+ make_fn_type(:a, :b), :b, :b),
+ Types::Optional.new(Types::Unresolved.new(:a)))
+ globalctx["Optional/build"] = BuiltinFunction.new { |a|
+ BuiltinFunction.new { |f|
+ just = BuiltinFunction.new { |x| x }
+ f.call(Types::Optional.new(a)).call(just).call(nil)
+ }
+ }
+
+
+ # Naturals:
+ natural_fold_type = make_fn_type(
+ Types::Natural,
+ [:a],
+ make_fn_type(:a, :a),
+ :a, :a)
+ globalctx["Natural/fold"] = BuiltinFunction.new { |n|
+ BuiltinFunction.new { |a|
+ BuiltinFunction.new { |succ|
+ BuiltinFunction.new { |zero|
+ res = zero
+ n.times { res = succ.call(res) }
+ res
+ }
+ }
+ }
+ }
+ types["Natural/fold"] = natural_fold_type
+
+ types["Natural/even"] = make_fn_type(Types::Natural, Types::Bool)
+ globalctx["Natural/even"] = BuiltinFunction.new { |n| n % 2 == 0 }
+
+ types["Natural/odd"] = make_fn_type(Types::Natural, Types::Bool)
+ globalctx["Natural/odd"] = BuiltinFunction.new { |n| n % 2 == 1 }
+
+ types["Natural/isZero"] = make_fn_type(Types::Natural, Types::Bool)
+ globalctx["Natural/isZero"] = BuiltinFunction.new { |n| n == 0 }
+
+ globalctx["Natural/build"] = BuiltinFunction.new { |f|
+ zero = 0
+ succ = BuiltinFunction.new { |n| n + 1 }
+ f.call(Types::Natural).call(succ).call(zero)
+ }
+ types["Natural/build"] = make_fn_type(make_fn_type([:a], make_fn_type(:a, :a), :a, :a), Types::Natural)
end
module_function :fill_context
diff --git a/lib/types.rb b/lib/types.rb
index 1e6bbee26f4eacf1214c09402f6369345a34d9c5..b36499edca49bf8bd90578e77dd1d868f8980558 100644
--- a/lib/types.rb
+++ b/lib/types.rb
@@ -23,7 +23,7 @@ module Dhallish
end
end
def to_s()
- if @metadata.nil? or @metadata.is_a? Unresolved
+ if @metadata.nil?
"Type"
else
"Type(#{@metadata.to_s})"
@@ -149,10 +149,16 @@ module Dhallish
end
end
+ # basically a substitution
def resolve(orgtype, name, newtype)
+ puts "resolve[#{name} => #{newtype}]:\t#{orgtype}"
case orgtype
when Type
- Type.new(resolve(orgtype.metadata, name, newtype))
+ if !orgtype.metadata.nil?
+ Type.new(resolve(orgtype.metadata, name, newtype))
+ else
+ Type.new(nil)
+ end
when Unresolved
if name == orgtype.name
newtype
@@ -160,17 +166,25 @@ module Dhallish
orgtype
end
when Function
- if orgtype.restype.nil?
- Function.new(resolve(orgtype.argtype, name, newtype), nil, orgtype.unres)
- else
- restype = orgtype.restype
- argtype = orgtype.argtype
- if orgtype.unres.nil? or orgtype.unres != name
- restype = resolve(orgtype.restype, name, newtype)
- argtype = resolve(orgtype.argtype, name, newtype)
- end
- Function.new(argtype, restype, orgtype.unres)
+ restype = orgtype.restype
+ argtype = orgtype.argtype
+ unres = orgtype.unres
+ if orgtype.unres.nil? or orgtype.unres != name
+ restype = resolve(orgtype.restype, name, newtype)
+ argtype = resolve(orgtype.argtype, name, newtype)
+ elsif !orgtype.unres.nil?
+ assert("debug: #{orgtype}") {
+ argtype == Type.new(Unresolved.new(orgtype.unres))
+ }
+ # TODO: resolve bug?
+ # Bug vielleicht in call node?
+ # `dhalltest.rb`
+ #puts "1--> #{Function.new(argtype, restype, unres)}"
+ #argtype = resolve(orgtype.argtype, name, newtype)
+ #unres = name
+ #puts "2--> #{Function.new(argtype, restype, unres)}"
end
+ Function.new(argtype, restype, unres)
when Optional
Optional.new(resolve(orgtype.type, name, newtype))
when List
@@ -180,6 +194,7 @@ module Dhallish
when Union
Union.new(orgtype.types.map{ |key, val| [key, resolve(val, name, newtype)] }.to_h)
else
+ assert("debug: #{orgtype}") { is_a_type?(orgtype) }
orgtype
end
end
@@ -188,82 +203,87 @@ module Dhallish
def unification(a, b, mapping={})
case a
when Type
- if b.is_a? Type
+ if !b.is_a? Type; return nil end
+ # I think those Kinds dhall has might be important here.
+ # `Type` is actually of type `Kind`?
+ # TODO: There is stuff to be done here i think...
+ if !a.metadata.nil? and !b.metadata.nil?
unification(a.metadata, b.metadata, mapping)
+ elsif b.metadata.nil?
+ mapping
else
nil
end
+ when Natural, Integer, Double, Text, Bool
+ if a == b; mapping else nil end
+ when List
+ if !b.is_a? List; return nil end
+ unification(a.type, b.type, mapping)
+ when Optional
+ if !b.is_a? Optional; return nil end
+ unification(a.type, b.type, mapping)
+ when Record
+ if !b.is_a? Record or a.types.length != b.types.length; return nil end
+ for key in a.types.keys
+ afield = a.types[key]
+ bfield = b.types[key]
+ if bfield.nil? or unification(afield, bfield, mapping).nil?
+ return nil
+ end
+ end
+ mapping
+ when Union
+ if !b.is_a? Union or a.types.length != b.types.length; return nil end
+ for key in a.types.keys
+ afield = a.types[key]
+ bfield = b.types[key]
+ if bfield.nil? or unification(afield, bfield, mapping).nil?
+ return nil
+ end
+ end
+ mapping
when Unresolved
- if b.is_a? Unresolved
- if mapping[a.name] == nil
- mapping[a.name] = b.name
+ if !b.is_a? Unresolved; return nil end
+ if mapping[a.name] != nil
+ if mapping[a.name] == b.name
mapping
- elsif mapping[a.name] != b.name
- nil
else
- mapping
+ nil
end
else
- nil
+ mapping[a.name] = b.name
+ mapping
end
when Function
- if b.is_a? Function
- if unification(a.argtype, b.argtype, mapping).nil?
- nil
- else
- arestype = a.restype
- brestype = b.restype
- if !a.unres.nil?; arestype = resolve(a.restype, a.unres, Unresolved.new(get_new_sym())) end
- if !b.unres.nil?; brestype = resolve(b.restype, b.unres, Unresolved.new(get_new_sym())) end
- unification(arestype, brestype, mapping)
+ if !b.is_a? Function; return nil end
+ if !a.unres.nil?
+ assert("debug: #{a}") { a.argtype.is_a? Type and a.argtype.metadata == Unresolved.new(a.unres) }
+ if b.unres.nil?; return nil end
+ assert("debug: #{b}") { b.argtype.is_a? Type and b.argtype.metadata == Unresolved.new(b.unres) }
+
+ # TODO: Update mapping?
+ # TODO: keys and values swapped?
+
+ arestype = a.restype
+ brestype = b.restype
+ if mapping.keys.include? a.unres
+ sym = get_new_sym()
+ arestype = resolve(arestype, a.unres, Unresolved.new(sym))
end
- else
- nil
- end
- when Optional
- if b.is_a? Optional
- unification(a.type, b.type, mapping)
- else
- nil
- end
- when List
- if b.is_a? List
- unification(a.type, b.type, mapping)
- else
- nil
- end
- when Record
- if b.is_a? Record and a.types.keys.length == b.types.keys.length
- for key in a.types.keys
- aelm = a.types[key]
- belm = b.types[key]
- if belm.nil?; return nil end
- ret = unification(aelm, belm, mapping)
- if ret.nil?; return nil end
+ if mapping.values.include? b.unres
+ sym = get_new_sym()
+ brestype = resolve(brestype, b.unres, Unresolved.new(sym))
end
- mapping
+ unification(arestype, brestype, mapping)
else
- nil
- end
- when Union
- if b.is_a? Union and a.types.keys.length == b.types.keys.length
- for key in a.types.keys
- aelm = a.types[key]
- belm = b.types[key]
- if belm.nil?; return nil end
- ret = unification(aelm, belm, mapping)
- if ret.nil?; return nil end
+ if !unification(a.argtype, b.argtype, mapping).nil?
+ unification(a.restype, b.restype, mapping)
+ else
+ nil
end
- mapping
- else
- nil
end
else
- if a == b
- mapping
- else
- nil
- end
+ raise "New Type? a: #{a.inspect}, b: #{b.inspect}"
end
end
module_function :unification
@@ -307,9 +327,11 @@ module Dhallish
"{#{ dhallval.map{ |key, val| "\"#{escape_str(key)}\": #{ to_json(val) }" }.join(", ") }}"
when nil
"null"
+ when Union
+ to_json(dhallval.init_val)
else
if Types::is_a_type? dhallval
- "\"#{dhallval.to_s}\""
+ "\"Dhallish::Type(#{dhallval.to_s})\""
else
"\"<#{escape_str(dhallval.class.to_s)}##{dhallval.hash.abs.to_s(16)}>\""
end
diff --git a/lib/utils.rb b/lib/utils.rb
index b03bd3ae75a5d117a379221e525d0e83a37704e8..300fb1e6152cfcefb74a668769f07d58dcac5a4d 100644
--- a/lib/utils.rb
+++ b/lib/utils.rb
@@ -16,7 +16,7 @@ end
$dhallish_internal_global_counter = 0
def get_new_sym()
$dhallish_internal_global_counter += 1
- ("__newsym_#{$dhallish_internal_global_counter}").to_sym
+ ("__#{$dhallish_internal_global_counter}").to_sym
end
def escape_str(str)
diff --git a/tests/test.rb b/tests/test.rb
index 6e1205583dde98b1117477db603d1e7d3026867d..c6cc00e66630af777adb3240493b9e4891762e08 100755
--- a/tests/test.rb
+++ b/tests/test.rb
@@ -36,7 +36,7 @@ positive_tests = {
"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 ],
- "union merge": [ "merge { Left = \\(x: Natural) -> Natural/show x, Right = \\(y: Integer) -> Integer/show y } < Left = 42 | Right : Integer >", "42" ]
+ "union merge": [ "merge { Left = \\(x: Natural) -> Natural/show x, Right = \\(y: Integer) -> Integer/show y } < Left = 42 | Right : Integer >", "42" ]
}
negative_tests = {
@@ -115,3 +115,12 @@ dhallfiles.each { |file|
print_msg(file, true)
end
}
+
+puts "\n=== Testing Typecheck on dhall-lang/Prelude/package.dhall"
+begin
+ Dhallish::evaluate("./" + File.join(File.dirname(__FILE__), "../dhall-lang/Prelude/package.dhall"))
+rescue
+ print_msg("Prelude/package.dhall", false)
+else
+ print_msg("Prelude/package.dhall", true)
+end