From fe1211268d7d4e34cb96e8fbcfdd344af5e4e37f Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Fri, 8 Mar 2019 23:05:04 -0500 Subject: [PATCH] Bare minimum refactor to pass the metrics --- lib/dhall/ast.rb | 26 +++++- lib/dhall/binary.rb | 15 +-- lib/dhall/builtins.rb | 201 +++++++++++++++++++---------------------- lib/dhall/normalize.rb | 44 ++++----- 4 files changed, 141 insertions(+), 145 deletions(-) diff --git a/lib/dhall/ast.rb b/lib/dhall/ast.rb index 918e557..f06c1fe 100644 --- a/lib/dhall/ast.rb +++ b/lib/dhall/ast.rb @@ -34,7 +34,12 @@ module Dhall end def *(other) - Operator::Times.new(lhs: self, rhs: other) + case other + when Natural + other * self + else + Operator::Times.new(lhs: self, rhs: other) + end end def <<(other) @@ -126,6 +131,16 @@ module Dhall body Expression end) + def self.of_arguments(*types, body:) + types.reverse.reduce(body) do |inner, type| + new( + var: "_", + type: type, + body: inner + ) + end + end + def map_subexpressions(&block) with(var: var, type: type.nil? ? nil : block[type], body: block[body]) end @@ -169,6 +184,10 @@ module Dhall name String, default: "_" index (0..Float::INFINITY), default: 0 end) + + def self.[](name, index=0) + new(name: name, index: index) + end end class Operator < Expression @@ -200,6 +219,10 @@ module Dhall elements ArrayOf(Expression) end) + def self.of(*args) + List.new(elements: args) + end + def map_subexpressions(&block) with(elements: elements.map(&block)) end @@ -562,6 +585,7 @@ module Dhall end def *(other) + return self if zero? if other.is_a?(Natural) with(value: value * other.value) else diff --git a/lib/dhall/binary.rb b/lib/dhall/binary.rb index 525da62..41fb6de 100644 --- a/lib/dhall/binary.rb +++ b/lib/dhall/binary.rb @@ -42,20 +42,15 @@ module Dhall class Function def self.decode(var_or_type, type_or_body, body_or_nil=nil) + type_or_body = Dhall.decode(type_or_body) + if body_or_nil.nil? - new( - var: "_", - type: Dhall.decode(var_or_type), - body: Dhall.decode(type_or_body) - ) + of_arguments(Dhall.decode(var_or_type), body: type_or_body) else raise ArgumentError, "explicit var named _" if var_or_type == "_" - new( - var: var_or_type, - type: Dhall.decode(type_or_body), - body: Dhall.decode(body_or_nil) - ) + body_or_nil = Dhall.decode(body_or_nil) + new(var: var_or_type, type: type_or_body, body: body_or_nil) end end end diff --git a/lib/dhall/builtins.rb b/lib/dhall/builtins.rb index a869487..fdc88cf 100644 --- a/lib/dhall/builtins.rb +++ b/lib/dhall/builtins.rb @@ -14,6 +14,33 @@ module Dhall Application.new(function: f, arguments: [arg]) end end + + protected + + def attributes + self.class.value_semantics.attributes + end + + def fill_next_if_valid(value) + with(attributes.each_with_object({}) do |attr, h| + if !send(attr.name).nil? + h[attr.name] = send(attr.name) + elsif attr.validate?(value) + h[attr.name] = value + value = nil + else + return nil + end + end) + end + + def full? + attributes.all? { |attr| !send(attr.name).nil? } + end + + def fill_or_call(arg, &block) + full? ? block[arg] : fill_next_if_valid(arg) + end end module Builtins @@ -64,13 +91,9 @@ module Dhall def call(arg) arg.call( Variable.new(name: "Natural"), - Function.new( - var: "_", - type: Variable.new(name: "Natural"), - body: Operator::Plus.new( - lhs: Variable.new(name: "_"), - rhs: Natural.new(value: 1) - ) + Function.of_arguments( + Variable.new(name: "Natural"), + body: Variable["_"] + Natural.new(value: 1) ), Natural.new(value: 0) ) @@ -88,28 +111,20 @@ module Dhall end class Natural_fold < Builtin - def initialize(nat=nil, type=nil, f=nil) - @nat = nat - @type = type - @f = f - end + include(ValueSemantics.for_attributes do + nat Either(nil, Natural), default: nil + type Either(nil, Expression), default: nil + f Either(nil, Expression), default: nil + end) def call(arg) - if @nat.nil? && arg.is_a?(Natural) - Natural_fold.new(arg) - elsif !@nat.nil? && @type.nil? - Natural_fold.new(@nat, arg) - elsif !@nat.nil? && !@type.nil? && @f.nil? - Natural_fold.new(@nat, @type, arg) - elsif !@nat.nil? && !@type.nil? && !@f.nil? + fill_or_call(arg) do if @nat.zero? arg.normalize else - @f.call(Natural_fold.new(@nat.pred, @type, @f).call(arg)) + @f.call(with(nat: nat.pred).call(arg)) end - else - super - end + end || super end end @@ -154,9 +169,9 @@ module Dhall end class List_build < Builtin - def initialize(type=nil) - @type = type - end + include(ValueSemantics.for_attributes do + type Either(nil, Expression), default: nil + end) def fusion(*args) _, arg, = args @@ -171,57 +186,38 @@ module Dhall end def call(arg) - if @type.nil? - self.class.new(arg) - else + fill_or_call(arg) do arg.call( - Application.new( - function: Variable.new(name: "List"), - arguments: [@type] - ), - Function.new( - var: "_", - type: @type, - body: Function.new( - var: "_", - type: Application.new( - function: Variable.new(name: "List"), - arguments: [@type.shift(1, "_", 0)] - ), - body: Operator::ListConcatenate.new( - lhs: List.new( - elements: [Variable.new(name: "_", index: 1)] - ), - rhs: Variable.new(name: "_") - ) - ) - ), - EmptyList.new(type: @type) + Variable["List"].call(type), + cons, + EmptyList.new(type: type) ) end end + + protected + + def cons + Function.of_arguments( + type, + Variable["List"].call(type.shift(1, "_", 0)), + body: List.of(Variable["_", 1]).concat(Variable["_"]) + ) + end end class List_fold < Builtin - def initialize(ltype=nil, list=nil, ztype=nil, f=nil) - @ltype = ltype - @list = list - @ztype = ztype - @f = f - end + include(ValueSemantics.for_attributes do + ltype Either(nil, Expression), default: nil + list Either(nil, List), default: nil + ztype Either(nil, Expression), default: nil + f Either(nil, Expression), default: nil + end) def call(arg) - if @ltype.nil? - List_fold.new(arg) - elsif @list.nil? - List_fold.new(@ltype, arg) - elsif @ztype.nil? - List_fold.new(@ltype, @list, arg) - elsif @f.nil? - List_fold.new(@ltype, @list, @ztype, arg) - else - @list.reduce(arg, &@f).normalize - end + fill_or_call(arg) do + list.reduce(arg, &f).normalize + end || super end end @@ -242,10 +238,7 @@ module Dhall "index" => Variable.new(name: "Natural"), "value" => arg.type )) do |x, idx| - Record.new( - "index" => Natural.new(value: idx), - "value" => x - ) + Record.new("index" => Natural.new(value: idx), "value" => x) end else super @@ -284,9 +277,9 @@ module Dhall end class Optional_build < Builtin - def initialize(type=nil) - @type = type - end + include(ValueSemantics.for_attributes do + type Either(nil, Expression), default: nil + end) def fusion(*args) _, arg, = args @@ -301,50 +294,38 @@ module Dhall end def call(arg) - if @type.nil? - self.class.new(arg) - else + fill_or_call(arg) do arg.call( - Application.new( - function: Variable.new(name: "Optional"), - arguments: [@type] - ), - Function.new( - var: "_", - type: @type, - body: Optional.new( - value: Variable.new(name: "_"), - type: @type - ) - ), - OptionalNone.new(type: @type) + Variable["Optional"].call(type), + some, + OptionalNone.new(type: type) ) end end + + protected + + def some + Function.of_arguments( + type, + body: Optional.new( + value: Variable["_"], + type: type + ) + ) + end end class Optional_fold < Builtin - def initialize(type=nil, optional=nil, ztype=nil, f=nil) - @type = type - @optional = optional - @ztype = ztype - @f = f - end + include(ValueSemantics.for_attributes do + type Either(nil, Expression), default: nil + optional Either(nil, Optional), default: nil + ztype Either(nil, Expression), default: nil + f Either(nil, Expression), default: nil + end) def call(arg) - if @type.nil? - self.class.new(arg) - elsif arg.is_a?(Optional) - self.class.new(@type, arg) - elsif !@optional.nil? && @ztype.nil? - self.class.new(@type, @optional, arg) - elsif !@optional.nil? && @f.nil? - self.class.new(@type, @optional, @ztype, arg) - elsif !@optional.nil? - @optional.reduce(arg, &@f) - else - super - end + fill_or_call(arg) { @optional.reduce(arg, &f) } || super end end diff --git a/lib/dhall/normalize.rb b/lib/dhall/normalize.rb index 0031b82..ebe3be1 100644 --- a/lib/dhall/normalize.rb +++ b/lib/dhall/normalize.rb @@ -132,10 +132,7 @@ module Dhall class Times def normalize normalized = super - if [normalized.lhs, normalized.rhs] - .any? { |x| x == Natural.new(value: 0) } - Natural.new(value: 0) - elsif normalized.lhs == Natural.new(value: 1) + if normalized.lhs == Natural.new(value: 1) normalized.rhs elsif normalized.rhs == Natural.new(value: 1) normalized.lhs @@ -255,18 +252,11 @@ module Dhall class If def normalize - if (pred = predicate.normalize).is_a?(Bool) - return pred.reduce(self.then, self.else) - end - - normalized = with( - predicate: pred, - then: self.then.normalize, - else: self.else.normalize - ) - - if normalized.trivial? - pred + normalized = super + if normalized.predicate.is_a?(Bool) + normalized.predicate.reduce(normalized.then, normalized.else) + elsif normalized.trivial? + normalized.predicate elsif normalized.then == normalized.else normalized.then else @@ -292,18 +282,24 @@ module Dhall class TextLiteral def normalize - chunks = super.chunks.flat_map { |chunk| - chunk.is_a?(TextLiteral) ? chunk.chunks : chunk - }.chunk { |x| x.is_a?(Text) }.flat_map do |(_, group)| - if group.first.is_a?(Text) - [Text.new(value: group.map(&:value).join)] - else - group + chunks = + super + .flatten.chunks.chunk { |x| x.is_a?(Text) }.flat_map do |(_, group)| + if group.first.is_a?(Text) + [Text.new(value: group.map(&:value).join)] + else + group + end end - end chunks.length == 1 ? chunks.first : with(chunks: chunks) end + + def flatten + with(chunks: chunks.flat_map do |chunk| + chunk.is_a?(TextLiteral) ? chunk.chunks : chunk + end) + end end class Import -- 2.45.2