diff --git a/.gitignore b/.gitignore index f992c97bea706f79570d7d27526f3e34f73f39ef..ef9be3c4785d2f9b4d1e4362889263bd1d54d792 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .bundle/ *.swp - - - +*.gem +lib/DhallishGrammar.rb diff --git a/Dhallish.gemspec b/Dhallish.gemspec index ae4bd72233190f39f10ea5e7ccb595c3f35dfb86..5d1a1d561afb6ac4aa184a4b4662d15bbf052076 100644 --- a/Dhallish.gemspec +++ b/Dhallish.gemspec @@ -5,9 +5,9 @@ Gem::Specification.new do |s| s.summary = "A Ruby Implementation of a Dhall-like Language" s.description = s.summary s.authors = ["Lou Knauer <lou.knauer@fau.de>", "Philipp Panzer <philipp.panzer@fau.de>"] - s.files = ["./bin", "./lib"] + s.files = Dir["./bin/*"] + Dir["./lib/*"] s.executables = ["dhallish"] s.homepage = "https://gitlab.cs.fau.de/basst/ruby-dhallish" - s.required_ruby_version = '>= 2.5' + s.required_ruby_version = '>= 2.4' s.add_runtime_dependency 'treetop', '~> 1.6.10' end diff --git a/benchmark.dhall b/benchmark.dhall new file mode 100644 index 0000000000000000000000000000000000000000..b856f459cbb38606e1ea6eba69fd90e6ab95de62 --- /dev/null +++ b/benchmark.dhall @@ -0,0 +1,11 @@ +let len = 10000 +let list = + Natural/fold len (List Natural) + (\(list: List Natural) -> + list # [Optional/fold Natural + (List/last Natural list) Natural (\(x: Natural) -> x + 1) 1]) + ([]: List Natural) +let sum = \(list: List Natural) -> + List/fold Natural list Natural (\(x: Natural) -> \(y: Natural) -> x + y) 0 +in + sum list diff --git a/bin/dhallish b/bin/dhallish index 8f0fc718653ffca4f92c638f318aa0fd0d92d5ec..ec2f54dacb64d7dc9d66a287970400c13f9a5fc5 100755 --- a/bin/dhallish +++ b/bin/dhallish @@ -1,12 +1,17 @@ #!/usr/bin/env ruby -this_file = File.readlink(__FILE__) +time1 = Time.now + +this_file = File.realpath(__FILE__) require File.expand_path("../lib/dhallish.rb", File.dirname(this_file)) require 'json' +time2 = Time.now + pretty_json = false show_type = false read_from_file = nil +measure_time = false ARGV.each { |arg| case arg @@ -14,6 +19,8 @@ ARGV.each { |arg| pretty_json = true when "--type" show_type = true + when "--time" + measure_time = true else if File.file?(arg) read_from_file = arg @@ -26,6 +33,8 @@ ARGV.each { |arg| dhall = Dhallish::Dhallish.new +time3 = Time.now + if read_from_file.nil? input = $stdin.read else @@ -34,6 +43,8 @@ else file.close end +time4 = Time.now + begin res, type = dhall.evaluate(input) rescue DhallError => err @@ -49,3 +60,11 @@ else puts Dhallish::to_json res end end + +time5 = Time.now +if measure_time + puts "" + puts "time loading dhallish.rb: #{time2 - time1}" + puts "time creating Dhallish-Instance: #{time3 - time2}" + puts "time for typecheck+evaluate: #{time5 - time4}" +end diff --git a/lib/dhallish.rb b/lib/dhallish.rb index 5af922ba8f85e87b887a4fd61f21f1b25735a7ac..5c9025b69ea8cba5b957a11636a91db9723d64fb 100644 --- a/lib/dhallish.rb +++ b/lib/dhallish.rb @@ -4,7 +4,14 @@ require_relative 'utils.rb' require_relative 'types.rb' require_relative 'stdlib.rb' -Treetop.load File.join(File.dirname(__FILE__), 'DhallishGrammar.treetop') +DIR = File.dirname(__FILE__) +if File.exists?(DIR + '/DhallishGrammar.rb') and File.mtime(DIR + '/DhallishGrammar.rb') >= File.mtime(DIR + '/DhallishGrammar.treetop') + require_relative 'DhallishGrammar.rb' +elsif File.writable?(DIR) and does_cmd_exist?('tt') and system("tt '#{DIR + '/DhallishGrammar.treetop'}'") + require_relative 'DhallishGrammar.rb' +else + Treetop.load File.join(DIR, 'DhallishGrammar.treetop') +end module Dhallish @@ -58,14 +65,14 @@ module Dhallish val end - def evaluate_in_ctx_from_file(file, dhallctx) + def evaluate_in_ctx_from_file(file, dhallctx, expected_type=nil) file = File.open(file) - res = evaluate_in_ctx(file.read, dhallctx) + res = evaluate_in_ctx(file.read, dhallctx, expected_type) file.close res end - def evaluate_in_ctx(dhallcode, dhallctx) + def evaluate_in_ctx(dhallcode, dhallctx, expected_type=nil) if !dhallctx.is_a? Hash or dhallctx["#valctx#"].nil? or dhallctx["#typectx#"].nil? raise DhallError, "Dhallish: evaluate_in_ctx must be called with a context returned by create_ctx" end @@ -73,6 +80,9 @@ module Dhallish valctx = dhallctx["#valctx#"] val, type = evaluate(dhallcode, valctx, typectx) + if expected_type != nil + assert("return type missmatch of `evaluate_in_ctx`") { type == expected_type } + end val end end diff --git a/lib/stdlib.rb b/lib/stdlib.rb index 351c6b4faac851d0fb3069798c09fd366ddbaebf..3371d0fd8da8c95a21d31f60164844d61c2ac38b 100644 --- a/lib/stdlib.rb +++ b/lib/stdlib.rb @@ -1,8 +1,6 @@ require_relative './types.rb' require_relative './utils.rb' -require "pry" - module Dhallish def make_fn_type(*args, rettype) diff --git a/lib/types.rb b/lib/types.rb index 2b2a86a371d16f78a33124cde9874dd40ca3428c..d99d256cf4c98ed61521de8bf357065426384a0f 100644 --- a/lib/types.rb +++ b/lib/types.rb @@ -22,7 +22,13 @@ module Dhallish true end end - def to_s() "Type(#{@metadata.to_s})" end + def to_s() + if @metadata.nil? or @metadata.is_a? Unresolved + "Type" + else + "Type(#{@metadata.to_s})" + end + end end class Optional # Type of Optional @@ -103,11 +109,11 @@ module Dhallish def to_s() if !@restype.nil? and !@unres.nil? - "∀(#{@unres}: #{@argtype.to_s} -> #{@restype.to_s})" + "∀(#{@unres}: #{@argtype.to_s}) -> #{@restype.to_s}" elsif !@restype.nil? - "∀(#{@argtype.to_s} -> #{@restype.to_s})" + "∀(#{@argtype.to_s}) -> #{@restype.to_s}" else - "∀(#{@argtype.to_s} -> ?)" + "∀(#{@argtype.to_s}) -> ?" end end end @@ -117,7 +123,7 @@ module Dhallish @name = name.to_sym end def ==(otype) otype.is_a? Unresolved and otype.name == @name end - def to_s() "Unresolved(#{@name})" end + def to_s() "#{@name}" end end class Union # types: label -> Type @@ -126,7 +132,7 @@ module Dhallish @types = types end - def ==(otype) + def ==(otype) if !otype.is_a? Union return false end @@ -136,7 +142,7 @@ module Dhallish } end - def to_s() + def to_s() "< #{@types.keys.map { |key| "#{key}: #{@types[key].to_s}" }.join(" | ")} >" @@ -301,7 +307,7 @@ module Dhallish if Types::is_a_type? dhallval "\"#{dhallval.to_s}\"" else - "\"Dhallish-Internal: #{escape_str(dhallval.inspect)}\"" + "\"<#{escape_str(dhallval.class.to_s)}##{dhallval.hash.abs.to_s(16)}>\"" end end end diff --git a/lib/utils.rb b/lib/utils.rb index bb05b08144b354d54485e0a64e421344bb4bfaf4..4e806d7d7ab2bae610f8562d323db7933663ed5d 100644 --- a/lib/utils.rb +++ b/lib/utils.rb @@ -22,3 +22,13 @@ end def escape_str(str) str.gsub("\n", "\\n").gsub("\"", "\\\"") end + +def does_cmd_exist?(cmd) + ENV['PATH'].split(File::PATH_SEPARATOR).each { |path| + exe = File.join(path, cmd) + if File.executable?(exe) && !File.directory?(exe) + return true + end + } + return false +end