dhallish 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/dhallish.rb ADDED
@@ -0,0 +1,70 @@
1
+
2
+ require 'treetop'
3
+ require_relative 'utils.rb'
4
+ require_relative 'types.rb'
5
+ require_relative 'stdlib.rb'
6
+
7
+ DIR = File.dirname(__FILE__)
8
+ if File.exists?(DIR + '/DhallishGrammar.rb') and File.mtime(DIR + '/DhallishGrammar.rb') >= File.mtime(DIR + '/DhallishGrammar.treetop')
9
+ require_relative 'DhallishGrammar.rb'
10
+ elsif File.writable?(DIR) and does_cmd_exist?('tt') and system("tt '#{DIR + '/DhallishGrammar.treetop'}'")
11
+ require_relative 'DhallishGrammar.rb'
12
+ else
13
+ Treetop.load File.join(DIR, 'DhallishGrammar.treetop')
14
+ end
15
+
16
+ module Dhallish
17
+
18
+ @@parser = DhallishGrammarParser.new
19
+
20
+ def empty_context(basedir=Dir.pwd)
21
+ ctx = { "<#TYPES#>" => Context.new, "<#VALS#>" => Context.new }
22
+ ctx["<#TYPES#>"]["<#DIR#>"] = basedir
23
+ fill_context(ctx["<#VALS#>"], ctx["<#TYPES#>"])
24
+ ctx
25
+ end
26
+ module_function :empty_context
27
+
28
+ def evaluate(dhallcode, ctx=nil, expected_type=nil)
29
+ if ctx.nil?; ctx = empty_context() end
30
+
31
+ rawast = @@parser.parse dhallcode
32
+ if rawast.nil?
33
+ raise DhallError, "#{@@parser.failure_reason} (line/column: #{@@parser.failure_line}:#{@@parser.failure_column})"
34
+ end
35
+
36
+ ast = rawast.to_node
37
+
38
+ type = ast.compute_type ctx["<#TYPES#>"]
39
+ res = ast.evaluate ctx["<#VALS#>"]
40
+
41
+ if !expected_type.nil?
42
+ if !(type == expected_type)
43
+ raise DhallError, "expression return type missmatch: expected `#{expected_type}`, got: `#{type}`"
44
+ else
45
+ res
46
+ end
47
+ else
48
+ [res, type]
49
+ end
50
+ end
51
+ module_function :evaluate
52
+
53
+ def create_ctx(dhallcode, basedir=Dir.pwd)
54
+ empty_ctx = empty_context(basedir)
55
+ ctx, type = evaluate(dhallcode, empty_ctx)
56
+ if type != Types::Record.new({}) or ctx["<#TYPES#>"].nil? or ctx["<#VALS#>"].nil?
57
+ raise DhallError, "return type of dhallcode supplied to `create_ctx` did not return a context (but something of type `#{type}`). use `???`"
58
+ end
59
+ ctx
60
+ end
61
+ module_function :create_ctx
62
+
63
+ def create_ctx_from_file(dhallfile, basedir=nil)
64
+ if basedir.nil?; basedir = File.dirname(dhallfile) end
65
+ file = File.open(dhallfile)
66
+ res = create_ctx(file.read, basedir)
67
+ file.close
68
+ res
69
+ end
70
+ end
data/lib/stdlib.rb ADDED
@@ -0,0 +1,268 @@
1
+ require_relative './types.rb'
2
+ require_relative './utils.rb'
3
+
4
+ module Dhallish
5
+
6
+ # Utility function to create dhallish function types:
7
+ # Use [:name] to introduce a new unresolved type and write
8
+ # :name to use it. Look at `fill_context` for examples.
9
+ def make_fn_type(*args, rettype)
10
+ if Types::not_a_type?(rettype); rettype = Types::Unresolved.new(rettype) end
11
+ args.reverse.reduce(rettype) { |rettype, arg|
12
+ argtype = arg
13
+ name = nil
14
+ if arg.is_a? Array
15
+ name = arg[0]
16
+ argtype = Types::Type.new(Types::Unresolved.new(name))
17
+ end
18
+ if Types::not_a_type?(argtype); argtype = Types::Unresolved.new(argtype) end
19
+ Types::Function.new(argtype, rettype, name)
20
+ }
21
+ end
22
+ module_function :make_fn_type
23
+
24
+ def fill_context(globalctx, types)
25
+
26
+ # Types:
27
+ globalctx["Natural"] = Types::Natural
28
+ globalctx["Integer"] = Types::Integer
29
+ globalctx["Double"] = Types::Double
30
+ globalctx["Bool"] = Types::Bool
31
+ globalctx["Text"] = Types::Text
32
+ globalctx["Type"] = Types::Type.new
33
+
34
+ types["Natural"] = Types::Type.new(Types::Natural)
35
+ types["Integer"] = Types::Type.new(Types::Integer)
36
+ types["Double"] = Types::Type.new(Types::Double)
37
+ types["Bool"] = Types::Type.new(Types::Bool)
38
+ types["Text"] = Types::Type.new(Types::Text)
39
+ types["Type"] = Types::Type.new(Types::Type.new)
40
+
41
+ # Shows:
42
+ globalctx["Natural/show"] = BuiltinFunction.new { |arg| arg.to_s }
43
+ types["Natural/show"] = Types::Function.new(Types::Natural, Types::Text)
44
+
45
+ globalctx["Integer/show"] = BuiltinFunction.new { |arg| arg.to_s }
46
+ types["Integer/show"] = Types::Function.new(Types::Integer, Types::Text)
47
+
48
+ globalctx["Double/show"] = BuiltinFunction.new { |arg| arg.to_s }
49
+ types["Double/show"] = Types::Function.new(Types::Double, Types::Text)
50
+
51
+ globalctx["Text/show"] = BuiltinFunction.new { |arg| arg }
52
+ types["Text/show"] = Types::Function.new(Types::Text, Types::Text)
53
+
54
+ # Casts:
55
+ natToInt = BuiltinFunction.new { |arg| arg }
56
+ globalctx["Natural/toInteger"] = natToInt
57
+ types["Natural/toInteger"] = Types::Function.new(Types::Natural, Types::Integer)
58
+
59
+ natToDou = BuiltinFunction.new { |arg| arg.to_f }
60
+ globalctx["Natural/toDouble"] = natToDou
61
+ types["Natural/toDouble"] = Types::Function.new(Types::Natural, Types::Double)
62
+
63
+
64
+ intToNat = BuiltinFunction.new { |arg| arg.abs }
65
+ globalctx["Integer/toNatural"] = intToNat
66
+ types["Integer/toNatural"] = Types::Function.new(Types::Integer, Types::Natural)
67
+
68
+ intToDou = BuiltinFunction.new { |arg| arg.to_f }
69
+ globalctx["Integer/toDouble"] = intToDou
70
+ types["Integer/toDouble"] = Types::Function.new(Types::Integer, Types::Double)
71
+
72
+
73
+ douToNat = BuiltinFunction.new { |arg| arg.round.abs }
74
+ globalctx["Double/toNatural"] = douToNat
75
+ types["Double/toNatural"] = Types::Function.new(Types::Double, Types::Natural)
76
+
77
+ douToInt = BuiltinFunction.new { |arg| arg.round }
78
+ globalctx["Double/toInteger"] = douToInt
79
+ types["Double/toInteger"] = Types::Function.new(Types::Double, Types::Integer)
80
+
81
+
82
+ # Lists:
83
+ globalctx["List"] = BuiltinFunction.new { |a| Types::List.new(a) }
84
+ types["List"] = make_fn_type([:list_a], Types::Type.new(Types::List.new(Types::Unresolved.new(:list_a))))
85
+
86
+ list_length_type = make_fn_type([:list_len_a], Types::List.new(Types::Unresolved.new(:list_len_a)), Types::Natural)
87
+ globalctx["List/length"] = BuiltinFunction.new{ |a|
88
+ BuiltinFunction.new { |list|
89
+ list.length
90
+ }
91
+ }
92
+ types["List/length"] = list_length_type
93
+
94
+ list_fold_type = make_fn_type(
95
+ [:list_fold_a],
96
+ Types::List.new(Types::Unresolved.new(:list_fold_a)),
97
+ [:list_fold_b],
98
+ make_fn_type(:list_fold_a, :list_fold_b, :list_fold_b),
99
+ :list_fold_b, :list_fold_b)
100
+ globalctx["List/fold"] = BuiltinFunction.new { |a|
101
+ BuiltinFunction.new { |list|
102
+ BuiltinFunction.new { |b|
103
+ BuiltinFunction.new { |f|
104
+ BuiltinFunction.new { |x|
105
+ acc = x
106
+ list.reverse_each { |elm| acc = f.call(elm).call(acc) }
107
+ acc
108
+ }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ types["List/fold"] = list_fold_type
114
+
115
+ list_head_type = make_fn_type(
116
+ [:list_head_a],
117
+ Types::List.new(Types::Unresolved.new(:list_head_a)),
118
+ Types::Optional.new(Types::Unresolved.new(:list_head_a)))
119
+ globalctx["List/head"] = BuiltinFunction.new { |a|
120
+ BuiltinFunction.new { |list|
121
+ list.first
122
+ }
123
+ }
124
+ types["List/head"] = list_head_type
125
+
126
+ list_last_type = make_fn_type(
127
+ [:list_last_a],
128
+ Types::List.new(Types::Unresolved.new(:list_last_a)),
129
+ Types::Optional.new(Types::Unresolved.new(:list_last_a)))
130
+ globalctx["List/last"] = BuiltinFunction.new { |a|
131
+ BuiltinFunction.new { |list|
132
+ list.last
133
+ }
134
+ }
135
+ types["List/last"] = list_last_type
136
+
137
+ list_tail_type = make_fn_type(
138
+ [:list_tail_a],
139
+ Types::List.new(Types::Unresolved.new(:list_tail_a)),
140
+ Types::List.new(Types::Unresolved.new(:list_tail_a)))
141
+ globalctx["List/tail"] = BuiltinFunction.new { |a|
142
+ BuiltinFunction.new { |list|
143
+ if list.empty?
144
+ []
145
+ else
146
+ list[1..list.length]
147
+ end
148
+ }
149
+ }
150
+ types["List/tail"] = list_tail_type
151
+
152
+ types["List/reverse"] = make_fn_type(
153
+ [:list_reverse_a],
154
+ Types::List.new(Types::Unresolved.new(:list_reverse_a)),
155
+ Types::List.new(Types::Unresolved.new(:list_reverse_a)))
156
+ globalctx["List/reverse"] = BuiltinFunction.new { |a|
157
+ BuiltinFunction.new { |list| list.reverse }
158
+ }
159
+
160
+ types["List/build"] = make_fn_type(
161
+ [:list_build_a],
162
+ make_fn_type(
163
+ [:list_build_b],
164
+ make_fn_type(:list_build_a, :list_build_b, :list_build_b), :list_build_b, :list_build_b),
165
+ Types::List.new(Types::Unresolved.new(:list_build_a)))
166
+ globalctx["List/build"] = BuiltinFunction.new { |a|
167
+ BuiltinFunction.new { |f|
168
+ cons = BuiltinFunction.new { |x|
169
+ BuiltinFunction.new { |list| [x] + list }
170
+ }
171
+ f.call(Types::List.new(a)).call(cons).call([])
172
+ }
173
+ }
174
+
175
+ types["List/indexed"] = make_fn_type(
176
+ [:list_indexed_a],
177
+ Types::List.new(Types::Unresolved.new(:list_indexed_a)),
178
+ Types::List.new(Types::Record.new({
179
+ "index" => Types::Natural,
180
+ "value" => Types::Unresolved.new(:list_indexed_a)})))
181
+ globalctx["List/indexed"] = BuiltinFunction.new { |a|
182
+ BuiltinFunction.new { |list|
183
+ list.map.with_index { |val, idx|
184
+ { "index" => idx, "value" => val }
185
+ }
186
+ }
187
+ }
188
+
189
+ # Optionals:
190
+ globalctx["Optional"] = BuiltinFunction.new { |a|
191
+ Types::Optional.new(a)
192
+ }
193
+ types["Optional"] = make_fn_type([:opt_a], Types::Type.new(Types::Optional.new(Types::Unresolved.new(:opt_a))))
194
+
195
+ optional_fold_type = make_fn_type(
196
+ [:opt_fold_a],
197
+ Types::Optional.new(Types::Unresolved.new(:opt_fold_a)),
198
+ [:opt_fold_b],
199
+ make_fn_type(:opt_fold_a, :opt_fold_b),
200
+ :opt_fold_b, :opt_fold_b)
201
+ globalctx["Optional/fold"] = BuiltinFunction.new { |a|
202
+ BuiltinFunction.new { |opt|
203
+ BuiltinFunction.new { |b|
204
+ BuiltinFunction.new { |f|
205
+ BuiltinFunction.new { |x|
206
+ if opt.nil?
207
+ x
208
+ else
209
+ f.call(opt)
210
+ end
211
+ }
212
+ }
213
+ }
214
+ }
215
+ }
216
+ types["Optional/fold"] = optional_fold_type
217
+
218
+ types["Optional/build"] = make_fn_type(
219
+ [:opt_build_a],
220
+ make_fn_type([:opt_build_b],
221
+ make_fn_type(:opt_build_a, :opt_build_b), :opt_build_b, :opt_build_b),
222
+ Types::Optional.new(Types::Unresolved.new(:opt_build_a)))
223
+ globalctx["Optional/build"] = BuiltinFunction.new { |a|
224
+ BuiltinFunction.new { |f|
225
+ just = BuiltinFunction.new { |x| x }
226
+ f.call(Types::Optional.new(a)).call(just).call(nil)
227
+ }
228
+ }
229
+
230
+
231
+ # Naturals:
232
+ natural_fold_type = make_fn_type(
233
+ Types::Natural,
234
+ [:nat_fold_a],
235
+ make_fn_type(:nat_fold_a, :nat_fold_a),
236
+ :nat_fold_a, :nat_fold_a)
237
+ globalctx["Natural/fold"] = BuiltinFunction.new { |n|
238
+ BuiltinFunction.new { |a|
239
+ BuiltinFunction.new { |succ|
240
+ BuiltinFunction.new { |zero|
241
+ res = zero
242
+ n.times { res = succ.call(res) }
243
+ res
244
+ }
245
+ }
246
+ }
247
+ }
248
+ types["Natural/fold"] = natural_fold_type
249
+
250
+ types["Natural/even"] = make_fn_type(Types::Natural, Types::Bool)
251
+ globalctx["Natural/even"] = BuiltinFunction.new { |n| n % 2 == 0 }
252
+
253
+ types["Natural/odd"] = make_fn_type(Types::Natural, Types::Bool)
254
+ globalctx["Natural/odd"] = BuiltinFunction.new { |n| n % 2 == 1 }
255
+
256
+ types["Natural/isZero"] = make_fn_type(Types::Natural, Types::Bool)
257
+ globalctx["Natural/isZero"] = BuiltinFunction.new { |n| n == 0 }
258
+
259
+ globalctx["Natural/build"] = BuiltinFunction.new { |f|
260
+ zero = 0
261
+ succ = BuiltinFunction.new { |n| n + 1 }
262
+ f.call(Types::Natural).call(succ).call(zero)
263
+ }
264
+ 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)
265
+
266
+ end
267
+ module_function :fill_context
268
+ end