dhall 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/COPYING +676 -0
- data/README.md +283 -0
- data/bin/json-to-dhall +8 -0
- data/bin/yaml-to-dhall +8 -0
- data/dhall.gemspec +36 -0
- data/lib/dhall.rb +37 -0
- data/lib/dhall/as_dhall.rb +195 -0
- data/lib/dhall/ast.rb +1559 -0
- data/lib/dhall/binary.rb +277 -0
- data/lib/dhall/builtins.rb +425 -0
- data/lib/dhall/normalize.rb +420 -0
- data/lib/dhall/parser.citrus +500 -0
- data/lib/dhall/parser.rb +601 -0
- data/lib/dhall/resolve.rb +383 -0
- data/lib/dhall/typecheck.rb +1209 -0
- data/lib/dhall/util.rb +130 -0
- data/lib/dhall/visitor.rb +23 -0
- metadata +163 -0
data/lib/dhall/binary.rb
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cbor"
|
4
|
+
require "digest/sha2"
|
5
|
+
|
6
|
+
require "dhall/ast"
|
7
|
+
require "dhall/builtins"
|
8
|
+
|
9
|
+
module Dhall
|
10
|
+
def self.from_binary(cbor_binary)
|
11
|
+
decode(CBOR.decode(cbor_binary))
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.decode(expression)
|
15
|
+
BINARY.each do |match, use|
|
16
|
+
return use[expression] if expression.is_a?(match)
|
17
|
+
end
|
18
|
+
|
19
|
+
raise "Unknown expression: #{expression.inspect}"
|
20
|
+
end
|
21
|
+
|
22
|
+
class Expression
|
23
|
+
def self.decode(*args)
|
24
|
+
return new(value: args.first) if args.length == 1
|
25
|
+
|
26
|
+
new(*args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_cbor(packer=nil)
|
30
|
+
if packer
|
31
|
+
packer.write(as_json)
|
32
|
+
packer
|
33
|
+
else
|
34
|
+
CBOR.encode(as_json)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
alias to_binary to_cbor
|
38
|
+
|
39
|
+
def digest(digest: Digest::SHA2.new(256))
|
40
|
+
(digest << normalize.to_binary).freeze
|
41
|
+
end
|
42
|
+
|
43
|
+
def cache_key
|
44
|
+
"sha256:#{digest.hexdigest}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Application
|
49
|
+
def self.decode(function, *args)
|
50
|
+
function = Dhall.decode(function)
|
51
|
+
args.map(&Dhall.method(:decode)).reduce(function) do |f, arg|
|
52
|
+
self.for(function: f, argument: arg)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Function
|
58
|
+
def self.decode(var_or_type, type_or_body, body_or_nil=nil)
|
59
|
+
type_or_body = Dhall.decode(type_or_body)
|
60
|
+
|
61
|
+
if body_or_nil.nil?
|
62
|
+
of_arguments(Dhall.decode(var_or_type), body: type_or_body)
|
63
|
+
else
|
64
|
+
raise ArgumentError, "explicit var named _" if var_or_type == "_"
|
65
|
+
|
66
|
+
body_or_nil = Dhall.decode(body_or_nil)
|
67
|
+
new(var: var_or_type, type: type_or_body, body: body_or_nil)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Operator
|
73
|
+
def self.decode(opcode, lhs, rhs)
|
74
|
+
OPERATORS[opcode].new(
|
75
|
+
lhs: Dhall.decode(lhs),
|
76
|
+
rhs: Dhall.decode(rhs)
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class List
|
82
|
+
def self.decode(type, *els)
|
83
|
+
type = type.nil? ? nil : Dhall.decode(type)
|
84
|
+
if els.empty?
|
85
|
+
EmptyList.new(element_type: type)
|
86
|
+
else
|
87
|
+
List.new(elements: els.map(&Dhall.method(:decode)), element_type: type)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Optional
|
93
|
+
def self.decode(type, value=nil)
|
94
|
+
if value.nil?
|
95
|
+
OptionalNone.new(value_type: Dhall.decode(type))
|
96
|
+
else
|
97
|
+
Optional.new(
|
98
|
+
value: Dhall.decode(value),
|
99
|
+
value_type: type.nil? ? type : Dhall.decode(type)
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class Merge
|
106
|
+
def self.decode(record, input, type=nil)
|
107
|
+
new(
|
108
|
+
record: Dhall.decode(record),
|
109
|
+
input: Dhall.decode(input),
|
110
|
+
type: type.nil? ? nil : Dhall.decode(type)
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class RecordType
|
116
|
+
def self.decode(record)
|
117
|
+
if record.empty?
|
118
|
+
EmptyRecordType.new
|
119
|
+
else
|
120
|
+
new(record: Hash[record.map { |k, v| [k, Dhall.decode(v)] }])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class Record
|
126
|
+
def self.decode(record)
|
127
|
+
if record.empty?
|
128
|
+
EmptyRecord.new
|
129
|
+
else
|
130
|
+
new(record: Hash[record.map { |k, v| [k, Dhall.decode(v)] }])
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class RecordSelection
|
136
|
+
def self.decode(record, selector)
|
137
|
+
new(record: Dhall.decode(record), selector: selector)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class RecordProjection
|
142
|
+
def self.decode(record, *selectors)
|
143
|
+
if selectors.empty?
|
144
|
+
EmptyRecordProjection.new(record: Dhall.decode(record))
|
145
|
+
else
|
146
|
+
new(record: Dhall.decode(record), selectors: selectors)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
class UnionType
|
152
|
+
def self.decode(record)
|
153
|
+
new(alternatives: Hash[record.map do |k, v|
|
154
|
+
[k, v.nil? ? v : Dhall.decode(v)]
|
155
|
+
end])
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class Union
|
160
|
+
def self.decode(tag, value, alternatives)
|
161
|
+
new(
|
162
|
+
tag: tag,
|
163
|
+
value: Dhall.decode(value),
|
164
|
+
alternatives: UnionType.decode(alternatives)
|
165
|
+
)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class If
|
170
|
+
def self.decode(pred, thn, els)
|
171
|
+
new(
|
172
|
+
predicate: Dhall.decode(pred),
|
173
|
+
then: Dhall.decode(thn),
|
174
|
+
else: Dhall.decode(els)
|
175
|
+
)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class TextLiteral
|
180
|
+
def self.decode(*chunks)
|
181
|
+
lead_text, *pairs = chunks
|
182
|
+
chunks =
|
183
|
+
[Text.new(value: lead_text)] +
|
184
|
+
pairs.each_slice(2).flat_map do |(e, t)|
|
185
|
+
[Dhall.decode(e), Text.new(value: t)]
|
186
|
+
end
|
187
|
+
|
188
|
+
chunks.length == 1 ? chunks.first : TextLiteral.new(chunks: chunks)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class Import
|
193
|
+
def self.decode(integrity_check, import_type, path_type, *parts)
|
194
|
+
parts[0] = Dhall.decode(parts[0]) if path_type < 2 && !parts[0].nil?
|
195
|
+
parts[0] = EnvironmentVariable.decode(parts[0]) if path_type == 6
|
196
|
+
|
197
|
+
new(
|
198
|
+
IntegrityCheck.new(*integrity_check),
|
199
|
+
IMPORT_TYPES[import_type],
|
200
|
+
PATH_TYPES[path_type].new(*parts)
|
201
|
+
)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class LetBlock
|
206
|
+
def self.decode(*parts)
|
207
|
+
body = Dhall.decode(parts.pop)
|
208
|
+
lets = parts.each_slice(3).map do |(var, type, assign)|
|
209
|
+
Let.new(
|
210
|
+
var: var,
|
211
|
+
assign: Dhall.decode(assign),
|
212
|
+
type: type.nil? ? nil : Dhall.decode(type)
|
213
|
+
)
|
214
|
+
end
|
215
|
+
|
216
|
+
self.for(lets: lets, body: body)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class TypeAnnotation
|
221
|
+
def self.decode(value, type)
|
222
|
+
new(value: Dhall.decode(value), type: Dhall.decode(type))
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
BINARY = {
|
227
|
+
::TrueClass => ->(e) { Bool.new(value: e) },
|
228
|
+
::FalseClass => ->(e) { Bool.new(value: e) },
|
229
|
+
::Float => ->(e) { Double.new(value: e) },
|
230
|
+
::String => ->(e) { Builtins::ALL[e]&.new || Variable.new(name: e) },
|
231
|
+
::Integer => ->(e) { Variable.new(index: e) },
|
232
|
+
::Array => lambda { |e|
|
233
|
+
if e.length == 2 && e.first.is_a?(::String)
|
234
|
+
Variable.new(name: e[0], index: e[1])
|
235
|
+
else
|
236
|
+
tag, *body = e
|
237
|
+
BINARY_TAGS[tag]&.decode(*body) ||
|
238
|
+
(raise "Unknown expression: #{e.inspect}")
|
239
|
+
end
|
240
|
+
},
|
241
|
+
::CBOR::Tagged => lambda { |e|
|
242
|
+
return Dhall.decode(e.value) if e.tag == 55799
|
243
|
+
|
244
|
+
raise "Unknown tag: #{e.inspect}"
|
245
|
+
}
|
246
|
+
}.freeze
|
247
|
+
|
248
|
+
BINARY_TAGS = [
|
249
|
+
Application,
|
250
|
+
Function,
|
251
|
+
Forall,
|
252
|
+
Operator,
|
253
|
+
List,
|
254
|
+
Optional,
|
255
|
+
Merge,
|
256
|
+
RecordType,
|
257
|
+
Record,
|
258
|
+
RecordSelection,
|
259
|
+
RecordProjection,
|
260
|
+
UnionType,
|
261
|
+
Union,
|
262
|
+
nil,
|
263
|
+
If,
|
264
|
+
Natural,
|
265
|
+
Integer,
|
266
|
+
nil,
|
267
|
+
TextLiteral,
|
268
|
+
nil,
|
269
|
+
nil,
|
270
|
+
nil,
|
271
|
+
nil,
|
272
|
+
nil,
|
273
|
+
Import,
|
274
|
+
LetBlock,
|
275
|
+
TypeAnnotation
|
276
|
+
].freeze
|
277
|
+
end
|
@@ -0,0 +1,425 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dhall/ast"
|
4
|
+
|
5
|
+
module Dhall
|
6
|
+
class Builtin < Expression
|
7
|
+
include(ValueSemantics.for_attributes {})
|
8
|
+
|
9
|
+
def call(*args)
|
10
|
+
# Do not auto-normalize builtins to avoid recursion loop
|
11
|
+
args.reduce(self) do |f, arg|
|
12
|
+
Application.new(function: f, argument: arg)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def unfill
|
17
|
+
attributes.reduce(self.class.new) do |f, attr|
|
18
|
+
if send(attr.name).nil?
|
19
|
+
f
|
20
|
+
else
|
21
|
+
Application.new(function: f, argument: send(attr.name))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def as_json
|
27
|
+
if (unfilled = unfill).class != self.class
|
28
|
+
unfilled.as_json
|
29
|
+
else
|
30
|
+
self.class.name&.split(/::/)&.last&.tr("_", "/").to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def attributes
|
37
|
+
self.class.value_semantics.attributes
|
38
|
+
end
|
39
|
+
|
40
|
+
def fill_next_if_valid(value)
|
41
|
+
with(attributes.each_with_object({}) do |attr, h|
|
42
|
+
if !send(attr.name).nil?
|
43
|
+
h[attr.name] = send(attr.name)
|
44
|
+
elsif attr.validate?(value)
|
45
|
+
h[attr.name] = value
|
46
|
+
value = nil
|
47
|
+
else
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
end)
|
51
|
+
end
|
52
|
+
|
53
|
+
def full?
|
54
|
+
attributes.all? { |attr| !send(attr.name).nil? }
|
55
|
+
end
|
56
|
+
|
57
|
+
def fill_or_call(arg, &block)
|
58
|
+
full? ? block[arg] : fill_next_if_valid(arg)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module Builtins
|
63
|
+
# rubocop:disable Style/ClassAndModuleCamelCase
|
64
|
+
|
65
|
+
class Double_show < Builtin
|
66
|
+
def call(arg)
|
67
|
+
if arg.is_a?(Double)
|
68
|
+
Text.new(value: arg.to_s)
|
69
|
+
else
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Integer_show < Builtin
|
76
|
+
def call(arg)
|
77
|
+
if arg.is_a?(Integer)
|
78
|
+
Text.new(value: arg.to_s)
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Integer_toDouble < Builtin
|
86
|
+
def call(arg)
|
87
|
+
if arg.is_a?(Integer)
|
88
|
+
Double.new(value: arg.value.to_f)
|
89
|
+
else
|
90
|
+
super
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class Natural_build < Builtin
|
96
|
+
def fusion(arg, *bogus)
|
97
|
+
if bogus.empty? &&
|
98
|
+
arg.is_a?(Application) &&
|
99
|
+
arg.function == Natural_fold.new
|
100
|
+
arg.argument
|
101
|
+
else
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def call(arg)
|
107
|
+
arg.call(
|
108
|
+
Variable.new(name: "Natural"),
|
109
|
+
Function.of_arguments(
|
110
|
+
Variable.new(name: "Natural"),
|
111
|
+
body: Variable["_"] + Natural.new(value: 1)
|
112
|
+
),
|
113
|
+
Natural.new(value: 0)
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class Natural_even < Builtin
|
119
|
+
def call(nat)
|
120
|
+
if nat.is_a?(Natural)
|
121
|
+
Bool.new(value: nat.even?)
|
122
|
+
else
|
123
|
+
super
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class Natural_fold < Builtin
|
129
|
+
include(ValueSemantics.for_attributes do
|
130
|
+
nat Either(nil, Natural), default: nil
|
131
|
+
type Either(nil, Expression), default: nil
|
132
|
+
f Either(nil, Expression), default: nil
|
133
|
+
end)
|
134
|
+
|
135
|
+
def call(arg)
|
136
|
+
fill_or_call(arg) do
|
137
|
+
if @nat.zero?
|
138
|
+
arg.normalize
|
139
|
+
else
|
140
|
+
@f.call(with(nat: nat.pred).call(arg))
|
141
|
+
end
|
142
|
+
end || super
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class Natural_isZero < Builtin
|
147
|
+
def call(nat)
|
148
|
+
if nat.is_a?(Natural)
|
149
|
+
Bool.new(value: nat.zero?)
|
150
|
+
else
|
151
|
+
super
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class Natural_odd < Builtin
|
157
|
+
def call(nat)
|
158
|
+
if nat.is_a?(Natural)
|
159
|
+
Bool.new(value: nat.odd?)
|
160
|
+
else
|
161
|
+
super
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class Natural_show < Builtin
|
167
|
+
def call(nat)
|
168
|
+
if nat.is_a?(Natural)
|
169
|
+
Text.new(value: nat.to_s)
|
170
|
+
else
|
171
|
+
super
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
class Natural_toInteger < Builtin
|
177
|
+
def call(nat)
|
178
|
+
if nat.is_a?(Natural)
|
179
|
+
Integer.new(value: nat.value)
|
180
|
+
else
|
181
|
+
super
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class List_build < Builtin
|
187
|
+
include(ValueSemantics.for_attributes do
|
188
|
+
type Either(nil, Expression), default: nil
|
189
|
+
end)
|
190
|
+
|
191
|
+
def fusion(*args)
|
192
|
+
_, arg, = args
|
193
|
+
if arg.is_a?(Application) &&
|
194
|
+
arg.function.is_a?(Application) &&
|
195
|
+
arg.function.function == List_fold.new
|
196
|
+
arg.argument
|
197
|
+
else
|
198
|
+
super
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def call(arg)
|
203
|
+
fill_or_call(arg) do
|
204
|
+
arg.call(
|
205
|
+
Variable["List"].call(type),
|
206
|
+
cons,
|
207
|
+
EmptyList.new(element_type: type)
|
208
|
+
)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
protected
|
213
|
+
|
214
|
+
def cons
|
215
|
+
Function.of_arguments(
|
216
|
+
type,
|
217
|
+
Variable["List"].call(type.shift(1, "_", 0)),
|
218
|
+
body: List.of(Variable["_", 1]).concat(Variable["_"])
|
219
|
+
)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
class List_fold < Builtin
|
224
|
+
include(ValueSemantics.for_attributes do
|
225
|
+
ltype Either(nil, Expression), default: nil
|
226
|
+
list Either(nil, List), default: nil
|
227
|
+
ztype Either(nil, Expression), default: nil
|
228
|
+
f Either(nil, Expression), default: nil
|
229
|
+
end)
|
230
|
+
|
231
|
+
def call(arg)
|
232
|
+
fill_or_call(arg) do
|
233
|
+
list.reduce(arg, &f).normalize
|
234
|
+
end || super
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
class List_head < Builtin
|
239
|
+
include(ValueSemantics.for_attributes do
|
240
|
+
type Either(nil, Expression), default: nil
|
241
|
+
end)
|
242
|
+
|
243
|
+
def call(arg)
|
244
|
+
fill_or_call(arg) do
|
245
|
+
if arg.is_a?(List)
|
246
|
+
arg.first
|
247
|
+
else
|
248
|
+
super
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
class List_indexed < Builtin
|
255
|
+
include(ValueSemantics.for_attributes do
|
256
|
+
type Either(nil, Expression), default: nil
|
257
|
+
end)
|
258
|
+
|
259
|
+
def call(arg)
|
260
|
+
fill_or_call(arg) do
|
261
|
+
if arg.is_a?(List)
|
262
|
+
_call(arg)
|
263
|
+
else
|
264
|
+
super
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
protected
|
270
|
+
|
271
|
+
def _call(arg)
|
272
|
+
arg.map(type: indexed_type(type)) { |x, idx|
|
273
|
+
Record.new(
|
274
|
+
record: {
|
275
|
+
"index" => Natural.new(value: idx),
|
276
|
+
"value" => x
|
277
|
+
}
|
278
|
+
)
|
279
|
+
}.normalize
|
280
|
+
end
|
281
|
+
|
282
|
+
def indexed_type(value_type)
|
283
|
+
RecordType.new(
|
284
|
+
record: {
|
285
|
+
"index" => Variable.new(name: "Natural"),
|
286
|
+
"value" => value_type
|
287
|
+
}
|
288
|
+
)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
class List_last < Builtin
|
293
|
+
include(ValueSemantics.for_attributes do
|
294
|
+
type Either(nil, Expression), default: nil
|
295
|
+
end)
|
296
|
+
|
297
|
+
def call(arg)
|
298
|
+
fill_or_call(arg) do
|
299
|
+
if arg.is_a?(List)
|
300
|
+
arg.last
|
301
|
+
else
|
302
|
+
super
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
class List_length < Builtin
|
309
|
+
include(ValueSemantics.for_attributes do
|
310
|
+
type Either(nil, Expression), default: nil
|
311
|
+
end)
|
312
|
+
|
313
|
+
def call(arg)
|
314
|
+
fill_or_call(arg) do
|
315
|
+
if arg.is_a?(List)
|
316
|
+
Natural.new(value: arg.length)
|
317
|
+
else
|
318
|
+
super
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
class List_reverse < Builtin
|
325
|
+
include(ValueSemantics.for_attributes do
|
326
|
+
type Either(nil, Expression), default: nil
|
327
|
+
end)
|
328
|
+
|
329
|
+
def call(arg)
|
330
|
+
fill_or_call(arg) do
|
331
|
+
if arg.is_a?(List)
|
332
|
+
arg.reverse
|
333
|
+
else
|
334
|
+
super
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
class Optional_build < Builtin
|
341
|
+
include(ValueSemantics.for_attributes do
|
342
|
+
type Either(nil, Expression), default: nil
|
343
|
+
end)
|
344
|
+
|
345
|
+
def fusion(*args)
|
346
|
+
_, arg, = args
|
347
|
+
if arg.is_a?(Application) &&
|
348
|
+
arg.function.is_a?(Application) &&
|
349
|
+
arg.function.function == Optional_fold.new
|
350
|
+
arg.argument
|
351
|
+
else
|
352
|
+
super
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def call(arg)
|
357
|
+
fill_or_call(arg) do
|
358
|
+
arg.call(
|
359
|
+
Variable["Optional"].call(type),
|
360
|
+
some,
|
361
|
+
OptionalNone.new(value_type: type)
|
362
|
+
)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
protected
|
367
|
+
|
368
|
+
def some
|
369
|
+
Function.of_arguments(
|
370
|
+
type,
|
371
|
+
body: Optional.new(
|
372
|
+
value: Variable["_"],
|
373
|
+
value_type: type
|
374
|
+
)
|
375
|
+
)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
class Optional_fold < Builtin
|
380
|
+
include(ValueSemantics.for_attributes do
|
381
|
+
type Either(nil, Expression), default: nil
|
382
|
+
optional Either(nil, Optional), default: nil
|
383
|
+
ztype Either(nil, Expression), default: nil
|
384
|
+
f Either(nil, Expression), default: nil
|
385
|
+
end)
|
386
|
+
|
387
|
+
def call(*args)
|
388
|
+
args.reduce(self) do |fold, arg|
|
389
|
+
fold.fill_or_call(arg) do
|
390
|
+
fold.optional.reduce(arg, &fold.f)
|
391
|
+
end || super
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
class Text_show < Builtin
|
397
|
+
ENCODE = (Hash.new { |_, x| "\\u%04x" % x.ord }).merge(
|
398
|
+
"\"" => "\\\"",
|
399
|
+
"\\" => "\\\\",
|
400
|
+
"\b" => "\\b",
|
401
|
+
"\f" => "\\f",
|
402
|
+
"\n" => "\\n",
|
403
|
+
"\r" => "\\r",
|
404
|
+
"\t" => "\\t"
|
405
|
+
)
|
406
|
+
|
407
|
+
def call(arg)
|
408
|
+
if arg.is_a?(Text)
|
409
|
+
Text.new(
|
410
|
+
value: "\"#{arg.value.gsub(
|
411
|
+
/["\$\\\b\f\n\r\t\u0000-\u001F]/,
|
412
|
+
&ENCODE
|
413
|
+
)}\""
|
414
|
+
)
|
415
|
+
else
|
416
|
+
super
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
# rubocop:enable Style/ClassAndModuleCamelCase
|
422
|
+
|
423
|
+
ALL = Hash[constants.map { |c| [c.to_s.tr("_", "/"), const_get(c)] }]
|
424
|
+
end
|
425
|
+
end
|