halunke 0.8.0 → 0.9.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 +4 -4
- data/Gemfile.lock +3 -1
- data/README.md +1 -0
- data/docs/index.md +1 -0
- data/examples/error.hal +6 -0
- data/examples/stdio.hal +6 -6
- data/exe/halunke +2 -2
- data/halunke.gemspec +1 -0
- data/lib/halunke/grammar.y +9 -9
- data/lib/halunke/herror.rb +51 -0
- data/lib/halunke/interpreter.rb +11 -3
- data/lib/halunke/nodes.rb +57 -23
- data/lib/halunke/parser.rb +9 -9
- data/lib/halunke/runtime/hclass.rb +32 -14
- data/lib/halunke/runtime/hfunction.rb +5 -3
- data/lib/halunke/runtime/hnative_object.rb +9 -2
- data/lib/halunke/runtime/hobject.rb +10 -3
- data/lib/halunke/runtime/hstring.rb +1 -1
- data/lib/halunke/runtime/hunassigned_bareword.rb +8 -1
- data/lib/halunke/source_code_position.rb +40 -0
- data/lib/halunke/tokenizer.rb +18 -18
- data/lib/halunke/tokenizer.rl +16 -16
- data/lib/halunke/version.rb +1 -1
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b782ee6ddb661b1ab07e7beadc086c692572ed3e2aa3a80a33b9c598ed70a93
|
4
|
+
data.tar.gz: f6aa3c8e8984a15ae7593f388354b69b8d9915a544acce6f1911aeebb171c40f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61e527ced02be4c0a7b182d9838539148be73683c9fe7421c4e55753a882052551f9e5c16a3ee5e7a75b2224ebcaaeb87e19c1eb51a041dd75dffdf93b331791
|
7
|
+
data.tar.gz: 05dc2c4b8f27b6c4a7bae7cf0bd18fbaac19eee8b85bb1c90d0db107ffcc6169eed686c7c3e74e66c3f2f7a0ebd1059200f8740b6e0cadd2ee82e88d77b8687b
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
halunke (0.
|
4
|
+
halunke (0.9.0)
|
5
5
|
rack (~> 2.0.4)
|
6
6
|
|
7
7
|
GEM
|
@@ -11,6 +11,7 @@ GEM
|
|
11
11
|
racc (1.4.15)
|
12
12
|
rack (2.0.6)
|
13
13
|
rake (12.3.2)
|
14
|
+
warning (0.10.1)
|
14
15
|
|
15
16
|
PLATFORMS
|
16
17
|
ruby
|
@@ -21,6 +22,7 @@ DEPENDENCIES
|
|
21
22
|
minitest (~> 5.11.3)
|
22
23
|
racc (~> 1.4.15)
|
23
24
|
rake (~> 12.3.2)
|
25
|
+
warning (~> 0.10.1)
|
24
26
|
|
25
27
|
BUNDLED WITH
|
26
28
|
1.17.2
|
data/README.md
CHANGED
data/docs/index.md
CHANGED
data/examples/error.hal
ADDED
data/examples/stdio.hal
CHANGED
@@ -5,9 +5,9 @@
|
|
5
5
|
(stdio puts true)
|
6
6
|
(stdio puts false)
|
7
7
|
|
8
|
-
(stdio
|
9
|
-
(stdio
|
10
|
-
(stdio
|
11
|
-
(stdio
|
12
|
-
(stdio
|
13
|
-
(stdio
|
8
|
+
(stdio examine "Hello World")
|
9
|
+
(stdio examine 5.2)
|
10
|
+
(stdio examine @["a" 2 "b" 3])
|
11
|
+
(stdio examine ["a" "b"])
|
12
|
+
(stdio examine true)
|
13
|
+
(stdio examine false)
|
data/exe/halunke
CHANGED
@@ -12,12 +12,12 @@ if ARGV.length == 0
|
|
12
12
|
puts "Halunke #{Halunke::VERSION} REPL. Ctrl+d to quit"
|
13
13
|
while (line = Readline.readline(">> ", true))
|
14
14
|
begin
|
15
|
-
value = interpreter.eval(line)
|
15
|
+
value = interpreter.eval(line, error_mode: :repl)
|
16
16
|
puts "=> #{value}" if value
|
17
17
|
rescue Exception => e
|
18
18
|
puts "An Error Occurred: #{e.message}"
|
19
19
|
end
|
20
20
|
end
|
21
21
|
else
|
22
|
-
interpreter.eval(File.read(ARGV[0]))
|
22
|
+
interpreter.eval(File.read(ARGV[0]), error_mode: :file, exit_on_error: true)
|
23
23
|
end
|
data/halunke.gemspec
CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
|
|
23
23
|
|
24
24
|
spec.add_dependency "rack", "~> 2.0.4"
|
25
25
|
|
26
|
+
spec.add_development_dependency "warning", "~> 0.10.1"
|
26
27
|
spec.add_development_dependency "bundler", "~> 1.17.2"
|
27
28
|
spec.add_development_dependency "rake", "~> 12.3.2"
|
28
29
|
spec.add_development_dependency "minitest", "~> 5.11.3"
|
data/lib/halunke/grammar.y
CHANGED
@@ -28,16 +28,16 @@ rule
|
|
28
28
|
;
|
29
29
|
|
30
30
|
Expression:
|
31
|
-
NUMBER { NumberNode.new(val[0]) }
|
32
|
-
| STRING { StringNode.new(val[0]) }
|
33
|
-
| BAREWORD { BarewordNode.new(val[0]) }
|
34
|
-
| UNASSIGNED_BAREWORD { UnassignedNode.new(BarewordNode.new(val[0])) }
|
31
|
+
NUMBER { NumberNode.new(*val[0]) }
|
32
|
+
| STRING { StringNode.new(*val[0]) }
|
33
|
+
| BAREWORD { BarewordNode.new(*val[0]) }
|
34
|
+
| UNASSIGNED_BAREWORD { UnassignedNode.new(BarewordNode.new(*val[0]), val[0][1], val[0][2]) }
|
35
35
|
| START_COMMENT Expressions END_COMMENT { Nodes.new([]) }
|
36
|
-
| OPEN_CURLY Expressions CLOSE_CURLY { FunctionNode.new(ArrayNode.new([]), val[1]) }
|
37
|
-
| OPEN_CURLY BAR Expressions BAR Expressions CLOSE_CURLY { FunctionNode.new(val[2].
|
38
|
-
| OPEN_PAREN Expression Expressions CLOSE_PAREN { MessageSendNode.new(val[1], val[2].
|
39
|
-
| OPEN_BRACKET Expressions CLOSE_BRACKET { val[1].
|
40
|
-
| OPEN_DICT_BRACKET Expressions CLOSE_BRACKET { val[1].
|
36
|
+
| OPEN_CURLY Expressions CLOSE_CURLY { FunctionNode.new(ArrayNode.new([]), val[1], val[0][1], val[2][2]) }
|
37
|
+
| OPEN_CURLY BAR Expressions BAR Expressions CLOSE_CURLY { FunctionNode.new(ArrayNode.new(val[2].nodes), val[4], val[0][1], val[5][2]) }
|
38
|
+
| OPEN_PAREN Expression Expressions CLOSE_PAREN { MessageSendNode.new(val[1], MessageNode.new(val[2].nodes), val[0][1], val[3][2]) }
|
39
|
+
| OPEN_BRACKET Expressions CLOSE_BRACKET { ArrayNode.new(val[1].nodes, val[0][1], val[2][2]) }
|
40
|
+
| OPEN_DICT_BRACKET Expressions CLOSE_BRACKET { DictionaryNode.new(val[1].nodes, val[0][1], val[2][2]) }
|
41
41
|
;
|
42
42
|
end
|
43
43
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "rubygems/text"
|
2
|
+
|
3
|
+
module Halunke
|
4
|
+
class HError < StandardError
|
5
|
+
attr_reader :message
|
6
|
+
attr_reader :source_code_position
|
7
|
+
|
8
|
+
def initialize(message, source_code_position)
|
9
|
+
@message = message
|
10
|
+
@source_code_position = source_code_position
|
11
|
+
super(message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class HUnknownMessage < HError
|
16
|
+
include Gem::Text
|
17
|
+
|
18
|
+
def initialize(receiver, message_name, receivable_messages, source_code_position)
|
19
|
+
message = [
|
20
|
+
"#{receiver} received the message `#{message_name}`. It doesn't know how to handle that.",
|
21
|
+
did_you_mean(receivable_messages, message_name)
|
22
|
+
].join("\n")
|
23
|
+
|
24
|
+
super(message, source_code_position)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def did_you_mean(receivable_messages, message_name)
|
30
|
+
guess = receivable_messages.min_by { |m| levenshtein_distance(m, message_name) }
|
31
|
+
|
32
|
+
if levenshtein_distance(guess, message_name) < 5
|
33
|
+
"Did you mean `#{guess}`?"
|
34
|
+
else
|
35
|
+
"It supports the following messages: #{receivable_messages.join(", ")}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class HBarewordAlreadyAssigned < HError
|
41
|
+
end
|
42
|
+
|
43
|
+
class HUnassignedBareword < HError
|
44
|
+
end
|
45
|
+
|
46
|
+
class HEmptyFunction < HError
|
47
|
+
end
|
48
|
+
|
49
|
+
class HUnknownAttribute < HError
|
50
|
+
end
|
51
|
+
end
|
data/lib/halunke/interpreter.rb
CHANGED
@@ -26,10 +26,18 @@ module Halunke
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
def eval(str)
|
29
|
+
def eval(str, error_mode: :raise, exit_on_error: false)
|
30
30
|
nodes = @parser.parse(str)
|
31
31
|
result = nodes.eval(root_context)
|
32
32
|
result.nil? ? nil : result.inspect(root_context)
|
33
|
+
rescue HError => err
|
34
|
+
raise err if error_mode == :raise
|
35
|
+
|
36
|
+
puts err.source_code_position.reveal(str, error_mode)
|
37
|
+
puts err.message
|
38
|
+
exit(1) if exit_on_error
|
39
|
+
|
40
|
+
nil
|
33
41
|
end
|
34
42
|
|
35
43
|
def preludes
|
@@ -48,14 +56,14 @@ module Halunke
|
|
48
56
|
end
|
49
57
|
|
50
58
|
def []=(name, value)
|
51
|
-
raise
|
59
|
+
raise FrozenError if key? name
|
52
60
|
@context[name] = value
|
53
61
|
end
|
54
62
|
|
55
63
|
def [](name)
|
56
64
|
@context.fetch(name)
|
57
65
|
rescue KeyError
|
58
|
-
raise
|
66
|
+
raise KeyError if @parent.nil?
|
59
67
|
@parent[name]
|
60
68
|
end
|
61
69
|
|
data/lib/halunke/nodes.rb
CHANGED
@@ -7,62 +7,84 @@ module Halunke
|
|
7
7
|
def empty?
|
8
8
|
nodes.empty?
|
9
9
|
end
|
10
|
-
|
11
|
-
def to_message
|
12
|
-
MessageNode.new(nodes)
|
13
|
-
end
|
14
|
-
|
15
|
-
def to_array
|
16
|
-
ArrayNode.new(nodes)
|
17
|
-
end
|
18
|
-
|
19
|
-
def to_dictionary
|
20
|
-
DictionaryNode.new(nodes)
|
21
|
-
end
|
22
10
|
end
|
23
11
|
|
24
|
-
NumberNode = Struct.new(:value) do
|
12
|
+
NumberNode = Struct.new(:value, :ts, :te) do
|
25
13
|
def eval(context)
|
26
14
|
context["Number"].create_instance(value)
|
27
15
|
end
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
other.is_a?(NumberNode) &&
|
19
|
+
value == other.value
|
20
|
+
end
|
28
21
|
end
|
29
22
|
|
30
|
-
StringNode = Struct.new(:value) do
|
23
|
+
StringNode = Struct.new(:value, :ts, :te) do
|
31
24
|
def eval(context)
|
32
25
|
context["String"].create_instance(value)
|
33
26
|
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
other.is_a?(StringNode) &&
|
30
|
+
value == other.value
|
31
|
+
end
|
34
32
|
end
|
35
33
|
|
36
|
-
BarewordNode = Struct.new(:value) do
|
34
|
+
BarewordNode = Struct.new(:value, :ts, :te) do
|
37
35
|
def eval(context)
|
38
36
|
context[value]
|
37
|
+
rescue KeyError
|
38
|
+
source_code_position = SourceCodePosition.new(ts, te)
|
39
|
+
raise HUnassignedBareword.new("Bareword '#{value} is unassigned", source_code_position)
|
40
|
+
end
|
41
|
+
|
42
|
+
def ==(other)
|
43
|
+
other.is_a?(BarewordNode) &&
|
44
|
+
value == other.value
|
39
45
|
end
|
40
46
|
end
|
41
47
|
|
42
|
-
UnassignedNode = Struct.new(:node) do
|
48
|
+
UnassignedNode = Struct.new(:node, :ts, :te) do
|
43
49
|
def eval(context)
|
44
|
-
context["UnassignedBareword"].create_instance(node.value)
|
50
|
+
context["UnassignedBareword"].create_instance(node.value, source_code_position: SourceCodePosition.new(ts, te))
|
51
|
+
end
|
52
|
+
|
53
|
+
def ==(other)
|
54
|
+
other.is_a?(UnassignedNode) &&
|
55
|
+
node == other.node
|
45
56
|
end
|
46
57
|
end
|
47
58
|
|
48
|
-
FunctionNode = Struct.new(:params, :body) do
|
59
|
+
FunctionNode = Struct.new(:params, :body, :ts, :te) do
|
49
60
|
def eval(context)
|
50
|
-
raise "This function would not return anything
|
61
|
+
raise HEmptyFunction.new("This function would not return anything, in Halunke every function needs to return something.", SourceCodePosition.new(ts, te)) if body.empty?
|
51
62
|
signature = params.nodes.map(&:node).map(&:value)
|
52
63
|
|
53
64
|
context["Function"].new(signature, lambda { |call_context|
|
54
65
|
body.eval(call_context)
|
55
66
|
})
|
56
67
|
end
|
68
|
+
|
69
|
+
def ==(other)
|
70
|
+
other.is_a?(FunctionNode) &&
|
71
|
+
params == other.params &&
|
72
|
+
body == other.body
|
73
|
+
end
|
57
74
|
end
|
58
75
|
|
59
|
-
ArrayNode = Struct.new(:nodes) do
|
76
|
+
ArrayNode = Struct.new(:nodes, :ts, :te) do
|
60
77
|
def eval(context)
|
61
78
|
context["Array"].create_instance(nodes.map { |node| node.eval(context) })
|
62
79
|
end
|
80
|
+
|
81
|
+
def ==(other)
|
82
|
+
other.is_a?(ArrayNode) &&
|
83
|
+
nodes == other.nodes
|
84
|
+
end
|
63
85
|
end
|
64
86
|
|
65
|
-
DictionaryNode = Struct.new(:nodes) do
|
87
|
+
DictionaryNode = Struct.new(:nodes, :ts, :te) do
|
66
88
|
def eval(context)
|
67
89
|
hash = {}
|
68
90
|
nodes.each_slice(2) do |key, value|
|
@@ -70,11 +92,23 @@ module Halunke
|
|
70
92
|
end
|
71
93
|
context["Dictionary"].create_instance(hash)
|
72
94
|
end
|
95
|
+
|
96
|
+
def ==(other)
|
97
|
+
other.is_a?(DictionaryNode) &&
|
98
|
+
nodes == other.nodes
|
99
|
+
end
|
73
100
|
end
|
74
101
|
|
75
|
-
MessageSendNode = Struct.new(:receiver, :message) do
|
102
|
+
MessageSendNode = Struct.new(:receiver, :message, :ts, :te) do
|
76
103
|
def eval(context)
|
77
|
-
receiver.eval(context).receive_message(context, *message.eval(context)
|
104
|
+
receiver.eval(context).receive_message(context, *message.eval(context),
|
105
|
+
source_code_position: SourceCodePosition.new(ts, te))
|
106
|
+
end
|
107
|
+
|
108
|
+
def ==(other)
|
109
|
+
other.is_a?(MessageSendNode) &&
|
110
|
+
receiver == other.receiver &&
|
111
|
+
message == other.message
|
78
112
|
end
|
79
113
|
end
|
80
114
|
|
data/lib/halunke/parser.rb
CHANGED
@@ -190,25 +190,25 @@ module_eval(<<'.,.,', 'grammar.y', 26)
|
|
190
190
|
|
191
191
|
module_eval(<<'.,.,', 'grammar.y', 30)
|
192
192
|
def _reduce_4(val, _values)
|
193
|
-
NumberNode.new(val[0])
|
193
|
+
NumberNode.new(*val[0])
|
194
194
|
end
|
195
195
|
.,.,
|
196
196
|
|
197
197
|
module_eval(<<'.,.,', 'grammar.y', 31)
|
198
198
|
def _reduce_5(val, _values)
|
199
|
-
StringNode.new(val[0])
|
199
|
+
StringNode.new(*val[0])
|
200
200
|
end
|
201
201
|
.,.,
|
202
202
|
|
203
203
|
module_eval(<<'.,.,', 'grammar.y', 32)
|
204
204
|
def _reduce_6(val, _values)
|
205
|
-
BarewordNode.new(val[0])
|
205
|
+
BarewordNode.new(*val[0])
|
206
206
|
end
|
207
207
|
.,.,
|
208
208
|
|
209
209
|
module_eval(<<'.,.,', 'grammar.y', 33)
|
210
210
|
def _reduce_7(val, _values)
|
211
|
-
UnassignedNode.new(BarewordNode.new(val[0]))
|
211
|
+
UnassignedNode.new(BarewordNode.new(*val[0]), val[0][1], val[0][2])
|
212
212
|
end
|
213
213
|
.,.,
|
214
214
|
|
@@ -220,31 +220,31 @@ module_eval(<<'.,.,', 'grammar.y', 34)
|
|
220
220
|
|
221
221
|
module_eval(<<'.,.,', 'grammar.y', 35)
|
222
222
|
def _reduce_9(val, _values)
|
223
|
-
FunctionNode.new(ArrayNode.new([]), val[1])
|
223
|
+
FunctionNode.new(ArrayNode.new([]), val[1], val[0][1], val[2][2])
|
224
224
|
end
|
225
225
|
.,.,
|
226
226
|
|
227
227
|
module_eval(<<'.,.,', 'grammar.y', 36)
|
228
228
|
def _reduce_10(val, _values)
|
229
|
-
FunctionNode.new(val[2].
|
229
|
+
FunctionNode.new(ArrayNode.new(val[2].nodes), val[4], val[0][1], val[5][2])
|
230
230
|
end
|
231
231
|
.,.,
|
232
232
|
|
233
233
|
module_eval(<<'.,.,', 'grammar.y', 37)
|
234
234
|
def _reduce_11(val, _values)
|
235
|
-
MessageSendNode.new(val[1], val[2].
|
235
|
+
MessageSendNode.new(val[1], MessageNode.new(val[2].nodes), val[0][1], val[3][2])
|
236
236
|
end
|
237
237
|
.,.,
|
238
238
|
|
239
239
|
module_eval(<<'.,.,', 'grammar.y', 38)
|
240
240
|
def _reduce_12(val, _values)
|
241
|
-
val[1].
|
241
|
+
ArrayNode.new(val[1].nodes, val[0][1], val[2][2])
|
242
242
|
end
|
243
243
|
.,.,
|
244
244
|
|
245
245
|
module_eval(<<'.,.,', 'grammar.y', 39)
|
246
246
|
def _reduce_13(val, _values)
|
247
|
-
val[1].
|
247
|
+
DictionaryNode.new(val[1].nodes, val[0][1], val[2][2])
|
248
248
|
end
|
249
249
|
.,.,
|
250
250
|
|
@@ -1,7 +1,11 @@
|
|
1
|
+
require "halunke/source_code_position"
|
2
|
+
require "halunke/herror"
|
3
|
+
|
1
4
|
module Halunke
|
2
5
|
module Runtime
|
3
6
|
class HClass
|
4
7
|
attr_reader :name
|
8
|
+
attr_reader :instance_methods
|
5
9
|
|
6
10
|
def initialize(name, allowed_attributes, instance_methods, class_methods, native)
|
7
11
|
@name = name
|
@@ -12,7 +16,7 @@ module Halunke
|
|
12
16
|
end
|
13
17
|
|
14
18
|
class << self
|
15
|
-
def receive_message(context, message_name, message_value)
|
19
|
+
def receive_message(context, message_name, message_value, source_code_position: NullSourceCodePosition.new)
|
16
20
|
case message_name
|
17
21
|
when "new attributes methods class_methods"
|
18
22
|
name = determine_name(message_value[0])
|
@@ -30,7 +34,8 @@ module Halunke
|
|
30
34
|
instance_methods = determine_methods(message_value[1])
|
31
35
|
class_methods = {}
|
32
36
|
else
|
33
|
-
|
37
|
+
known_messages = ["new attributes methods class_methods", "new attributes methods", "new methods"]
|
38
|
+
raise HUnknownMessage.new("Class", message_name, known_messages, source_code_position)
|
34
39
|
end
|
35
40
|
|
36
41
|
context[name] = HClass.new(name, allowed_attributes, instance_methods, class_methods, false)
|
@@ -47,7 +52,17 @@ module Halunke
|
|
47
52
|
end
|
48
53
|
|
49
54
|
def determine_methods(hdictionary)
|
50
|
-
instance_methods = {
|
55
|
+
instance_methods = {
|
56
|
+
"inspect" => HFunction.new([:self], lambda { |context|
|
57
|
+
hself = context["self"]
|
58
|
+
|
59
|
+
attributes = hself.dict.map do |key, value|
|
60
|
+
%Q{"#{key}" #{value.inspect(context)}}
|
61
|
+
end
|
62
|
+
|
63
|
+
HString.create_instance("#{hself.runtime_class.name} @[#{attributes.join(' ')}]")
|
64
|
+
})
|
65
|
+
}
|
51
66
|
hdictionary.ruby_value.each_pair do |method_name, fn|
|
52
67
|
instance_methods[method_name.ruby_value] = fn
|
53
68
|
end
|
@@ -55,14 +70,15 @@ module Halunke
|
|
55
70
|
end
|
56
71
|
end
|
57
72
|
|
58
|
-
def receive_message(context, message_name, message_value)
|
73
|
+
def receive_message(context, message_name, message_value, source_code_position: NullSourceCodePosition.new)
|
59
74
|
if message_name == "new" && !native?
|
60
|
-
create_instance(message_value[0])
|
61
|
-
elsif @class_methods.
|
75
|
+
create_instance(message_value[0], source_code_position: source_code_position)
|
76
|
+
elsif @class_methods.key? message_name
|
62
77
|
m = @class_methods[message_name]
|
63
|
-
m.receive_message(context, "call", [HArray.create_instance([self].concat(message_value))]
|
78
|
+
m.receive_message(context, "call", [HArray.create_instance([self].concat(message_value))],
|
79
|
+
source_code_position: source_code_position)
|
64
80
|
else
|
65
|
-
raise
|
81
|
+
raise HUnknownMessage.new(self.inspect(context), message_name, @class_methods.keys, source_code_position)
|
66
82
|
end
|
67
83
|
end
|
68
84
|
|
@@ -70,22 +86,24 @@ module Halunke
|
|
70
86
|
@allowed_attributes.include? attribute_name
|
71
87
|
end
|
72
88
|
|
73
|
-
def create_instance(value = nil)
|
89
|
+
def create_instance(value = nil, source_code_position: NullSourceCodePosition.new)
|
74
90
|
if native?
|
75
|
-
HNativeObject.new(self, value)
|
91
|
+
HNativeObject.new(self, value, source_code_position: source_code_position)
|
76
92
|
else
|
77
|
-
HObject.new(self, value ? value.ruby_value : {})
|
93
|
+
HObject.new(self, value ? value.ruby_value : {}, source_code_position: source_code_position)
|
78
94
|
end
|
79
95
|
end
|
80
96
|
|
81
97
|
def lookup(message)
|
82
98
|
@instance_methods.fetch(message)
|
83
|
-
rescue KeyError
|
84
|
-
raise "Class #{@name} has no method to respond to message '#{message}'"
|
85
99
|
end
|
86
100
|
|
87
101
|
def inspect(_context)
|
88
|
-
"
|
102
|
+
"Class #{@name}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def runtime_class
|
106
|
+
self
|
89
107
|
end
|
90
108
|
|
91
109
|
def native?
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "halunke/source_code_position"
|
2
|
+
|
1
3
|
module Halunke
|
2
4
|
module Runtime
|
3
5
|
class HFunction
|
@@ -6,8 +8,8 @@ module Halunke
|
|
6
8
|
@fn = fn
|
7
9
|
end
|
8
10
|
|
9
|
-
def receive_message(parent_context, message_name, message_value)
|
10
|
-
raise
|
11
|
+
def receive_message(parent_context, message_name, message_value, source_code_position: NullSourceCodePosition.new)
|
12
|
+
raise HUnknownMessage.new(self.inspect(parent_context), message_name, ["call"], source_code_position) unless message_name == "call"
|
11
13
|
|
12
14
|
context = parent_context.create_child
|
13
15
|
args = message_value[0].ruby_value
|
@@ -20,7 +22,7 @@ module Halunke
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def inspect(_context)
|
23
|
-
"
|
25
|
+
"Function (#{@signature.length})"
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -1,16 +1,23 @@
|
|
1
|
+
require "halunke/source_code_position"
|
2
|
+
|
1
3
|
module Halunke
|
2
4
|
module Runtime
|
3
5
|
class HNativeObject
|
6
|
+
attr_reader :runtime_class
|
4
7
|
attr_reader :ruby_value
|
8
|
+
attr_reader :source_code_position
|
5
9
|
|
6
|
-
def initialize(runtime_class, ruby_value
|
10
|
+
def initialize(runtime_class, ruby_value, source_code_position:)
|
7
11
|
@runtime_class = runtime_class
|
8
12
|
@ruby_value = ruby_value
|
13
|
+
@source_code_position = source_code_position
|
9
14
|
end
|
10
15
|
|
11
|
-
def receive_message(context, message_name, message_value)
|
16
|
+
def receive_message(context, message_name, message_value, source_code_position: NullSourceCodePosition.new)
|
12
17
|
m = @runtime_class.lookup(message_name)
|
13
18
|
m.receive_message(context, "call", [HArray.create_instance([self].concat(message_value))])
|
19
|
+
rescue KeyError
|
20
|
+
raise HUnknownMessage.new(self.inspect(context), message_name, @runtime_class.instance_methods.keys, source_code_position) if m.nil?
|
14
21
|
end
|
15
22
|
|
16
23
|
def inspect(context)
|
@@ -1,25 +1,32 @@
|
|
1
|
+
require "halunke/source_code_position"
|
2
|
+
|
1
3
|
module Halunke
|
2
4
|
module Runtime
|
3
5
|
class HObject
|
4
6
|
attr_reader :dict
|
7
|
+
attr_reader :runtime_class
|
8
|
+
attr_reader :source_code_position
|
5
9
|
|
6
|
-
def initialize(runtime_class, dict)
|
10
|
+
def initialize(runtime_class, dict, source_code_position:)
|
7
11
|
@runtime_class = runtime_class
|
8
12
|
@dict = {}
|
13
|
+
@source_code_position = source_code_position
|
9
14
|
dict.each_pair do |hkey, value|
|
10
15
|
key = hkey.ruby_value
|
11
|
-
raise "Unknown attribute '#{key}' for #{@runtime_class.name}" unless @runtime_class.allowed_attribute? key
|
16
|
+
raise HUnknownAttribute.new("Unknown attribute '#{key}' for #{@runtime_class.name}", source_code_position) unless @runtime_class.allowed_attribute? key
|
12
17
|
@dict[key] = value
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
16
|
-
def receive_message(context, message_name, message_value)
|
21
|
+
def receive_message(context, message_name, message_value, source_code_position: NullSourceCodePosition.new)
|
17
22
|
if message_name == "@ else"
|
18
23
|
@dict.fetch(message_value[0].ruby_value, message_value[1])
|
19
24
|
else
|
20
25
|
m = @runtime_class.lookup(message_name)
|
21
26
|
m.receive_message(context, "call", [HArray.create_instance([self].concat(message_value))])
|
22
27
|
end
|
28
|
+
rescue KeyError
|
29
|
+
raise HUnknownMessage.new(self.inspect(context), message_name, @runtime_class.instance_methods.keys, source_code_position)
|
23
30
|
end
|
24
31
|
|
25
32
|
def inspect(context)
|
@@ -25,7 +25,7 @@ module Halunke
|
|
25
25
|
h[HString.create_instance(key)] = HString.create_instance(value)
|
26
26
|
end
|
27
27
|
end
|
28
|
-
HDictionary.create_instance(h)
|
28
|
+
HDictionary.create_instance(h, source_code_position: NullSourceCodePosition.new)
|
29
29
|
}),
|
30
30
|
"scan" => HFunction.new([:self, :regexp], lambda { |context|
|
31
31
|
result = context["self"].ruby_value.scan(context["regexp"].ruby_value)
|
@@ -5,7 +5,14 @@ module Halunke
|
|
5
5
|
[],
|
6
6
|
{
|
7
7
|
"=" => HFunction.new([:self, :other], lambda { |context|
|
8
|
-
context
|
8
|
+
bareword_name = context["self"].ruby_value
|
9
|
+
|
10
|
+
begin
|
11
|
+
context.parent[bareword_name] = context["other"]
|
12
|
+
rescue
|
13
|
+
assigned_value = context.parent[context["self"].ruby_value].inspect(context)
|
14
|
+
raise HBarewordAlreadyAssigned.new("Bareword '#{bareword_name} is already assigned to #{assigned_value}. In Halunke, you can only assign once.", context["self"].source_code_position)
|
15
|
+
end
|
9
16
|
context["true"]
|
10
17
|
}),
|
11
18
|
"inspect" => HFunction.new([:self], lambda { |context|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Halunke
|
2
|
+
class SourceCodePosition
|
3
|
+
def initialize(ts, te)
|
4
|
+
@ts = ts
|
5
|
+
@te = te
|
6
|
+
end
|
7
|
+
|
8
|
+
def reveal(source, error_mode)
|
9
|
+
line, line_number = source.lines.each_with_index do |candidate, candidate_line_number|
|
10
|
+
break candidate, candidate_line_number if @ts < candidate.length
|
11
|
+
|
12
|
+
@ts -= candidate.length
|
13
|
+
@te -= candidate.length
|
14
|
+
end
|
15
|
+
|
16
|
+
if @te > line.length
|
17
|
+
@te = line.length - 2
|
18
|
+
ellipsis = '...'
|
19
|
+
end
|
20
|
+
|
21
|
+
prefix = error_mode == :repl ? ">> " : "#{line_number + 1} | "
|
22
|
+
|
23
|
+
output = []
|
24
|
+
output << [prefix, line.rstrip, ellipsis].join("") if error_mode == :file
|
25
|
+
output << " " * (@ts + prefix.length) + "^" * (@te - @ts + 1)
|
26
|
+
output << "\n"
|
27
|
+
|
28
|
+
output
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class NullSourceCodePosition
|
33
|
+
def initialize
|
34
|
+
end
|
35
|
+
|
36
|
+
def reveal(source, error_mode)
|
37
|
+
"(Can't find the source code position, sorry)"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/halunke/tokenizer.rb
CHANGED
@@ -285,73 +285,73 @@ when 5 then
|
|
285
285
|
# line 23 "lib/halunke/tokenizer.rl"
|
286
286
|
begin
|
287
287
|
te = p+1
|
288
|
-
begin emit(:STRING, data[ts+1...te-1]) end
|
288
|
+
begin emit(:STRING, data[ts+1...te-1], ts, te - 1) end
|
289
289
|
end
|
290
290
|
when 6 then
|
291
291
|
# line 25 "lib/halunke/tokenizer.rl"
|
292
292
|
begin
|
293
293
|
te = p+1
|
294
|
-
begin emit(:BAREWORD, data[ts...te]) end
|
294
|
+
begin emit(:BAREWORD, data[ts...te], ts, te - 1) end
|
295
295
|
end
|
296
296
|
when 7 then
|
297
297
|
# line 26 "lib/halunke/tokenizer.rl"
|
298
298
|
begin
|
299
299
|
te = p+1
|
300
|
-
begin emit(:OPEN_PAREN, data[ts...te]) end
|
300
|
+
begin emit(:OPEN_PAREN, data[ts...te], ts, te - 1) end
|
301
301
|
end
|
302
302
|
when 8 then
|
303
303
|
# line 27 "lib/halunke/tokenizer.rl"
|
304
304
|
begin
|
305
305
|
te = p+1
|
306
|
-
begin emit(:CLOSE_PAREN, data[ts...te]) end
|
306
|
+
begin emit(:CLOSE_PAREN, data[ts...te], ts, te - 1) end
|
307
307
|
end
|
308
308
|
when 9 then
|
309
309
|
# line 28 "lib/halunke/tokenizer.rl"
|
310
310
|
begin
|
311
311
|
te = p+1
|
312
|
-
begin emit(:OPEN_CURLY, data[ts...te]) end
|
312
|
+
begin emit(:OPEN_CURLY, data[ts...te], ts, te - 1) end
|
313
313
|
end
|
314
314
|
when 10 then
|
315
315
|
# line 29 "lib/halunke/tokenizer.rl"
|
316
316
|
begin
|
317
317
|
te = p+1
|
318
|
-
begin emit(:CLOSE_CURLY, data[ts...te]) end
|
318
|
+
begin emit(:CLOSE_CURLY, data[ts...te], ts, te - 1) end
|
319
319
|
end
|
320
320
|
when 11 then
|
321
321
|
# line 30 "lib/halunke/tokenizer.rl"
|
322
322
|
begin
|
323
323
|
te = p+1
|
324
|
-
begin emit(:OPEN_BRACKET, data[ts...te]) end
|
324
|
+
begin emit(:OPEN_BRACKET, data[ts...te], ts, te - 1) end
|
325
325
|
end
|
326
326
|
when 12 then
|
327
327
|
# line 31 "lib/halunke/tokenizer.rl"
|
328
328
|
begin
|
329
329
|
te = p+1
|
330
|
-
begin emit(:CLOSE_BRACKET, data[ts...te]) end
|
330
|
+
begin emit(:CLOSE_BRACKET, data[ts...te], ts, te - 1) end
|
331
331
|
end
|
332
332
|
when 13 then
|
333
333
|
# line 32 "lib/halunke/tokenizer.rl"
|
334
334
|
begin
|
335
335
|
te = p+1
|
336
|
-
begin emit(:OPEN_DICT_BRACKET, data[ts...te]) end
|
336
|
+
begin emit(:OPEN_DICT_BRACKET, data[ts...te], ts, te - 1) end
|
337
337
|
end
|
338
338
|
when 14 then
|
339
339
|
# line 33 "lib/halunke/tokenizer.rl"
|
340
340
|
begin
|
341
341
|
te = p+1
|
342
|
-
begin emit(:START_COMMENT, data[ts...te]) end
|
342
|
+
begin emit(:START_COMMENT, data[ts...te], ts, te - 1) end
|
343
343
|
end
|
344
344
|
when 15 then
|
345
345
|
# line 34 "lib/halunke/tokenizer.rl"
|
346
346
|
begin
|
347
347
|
te = p+1
|
348
|
-
begin emit(:END_COMMENT, data[ts...te]) end
|
348
|
+
begin emit(:END_COMMENT, data[ts...te], ts, te - 1) end
|
349
349
|
end
|
350
350
|
when 16 then
|
351
351
|
# line 35 "lib/halunke/tokenizer.rl"
|
352
352
|
begin
|
353
353
|
te = p+1
|
354
|
-
begin emit(:BAR, data[ts...te]) end
|
354
|
+
begin emit(:BAR, data[ts...te], ts, te - 1) end
|
355
355
|
end
|
356
356
|
when 17 then
|
357
357
|
# line 36 "lib/halunke/tokenizer.rl"
|
@@ -368,13 +368,13 @@ when 19 then
|
|
368
368
|
# line 22 "lib/halunke/tokenizer.rl"
|
369
369
|
begin
|
370
370
|
te = p
|
371
|
-
p = p - 1; begin emit(:NUMBER, data[ts...te].to_r) end
|
371
|
+
p = p - 1; begin emit(:NUMBER, data[ts...te].to_r, ts, te - 1) end
|
372
372
|
end
|
373
373
|
when 20 then
|
374
374
|
# line 25 "lib/halunke/tokenizer.rl"
|
375
375
|
begin
|
376
376
|
te = p
|
377
|
-
p = p - 1; begin emit(:BAREWORD, data[ts...te]) end
|
377
|
+
p = p - 1; begin emit(:BAREWORD, data[ts...te], ts, te - 1) end
|
378
378
|
end
|
379
379
|
when 21 then
|
380
380
|
# line 37 "lib/halunke/tokenizer.rl"
|
@@ -386,7 +386,7 @@ when 22 then
|
|
386
386
|
# line 22 "lib/halunke/tokenizer.rl"
|
387
387
|
begin
|
388
388
|
begin p = ((te))-1; end
|
389
|
-
begin emit(:NUMBER, data[ts...te].to_r) end
|
389
|
+
begin emit(:NUMBER, data[ts...te].to_r, ts, te - 1) end
|
390
390
|
end
|
391
391
|
when 23 then
|
392
392
|
# line 37 "lib/halunke/tokenizer.rl"
|
@@ -400,7 +400,7 @@ when 24 then
|
|
400
400
|
case act
|
401
401
|
when 3 then
|
402
402
|
begin begin p = ((te))-1; end
|
403
|
-
emit(:UNASSIGNED_BAREWORD, data[ts+1 ...te]) end
|
403
|
+
emit(:UNASSIGNED_BAREWORD, data[ts+1 ...te], ts, te - 1) end
|
404
404
|
when 16 then
|
405
405
|
begin begin p = ((te))-1; end
|
406
406
|
raise "Could not lex '#{ data[ts...te] }'" end
|
@@ -460,8 +460,8 @@ end
|
|
460
460
|
|
461
461
|
private
|
462
462
|
|
463
|
-
def emit(type, value)
|
464
|
-
@tokens << [ type, value ]
|
463
|
+
def emit(type, value, ts, te)
|
464
|
+
@tokens << [ type, [ value, ts, te ] ]
|
465
465
|
end
|
466
466
|
end
|
467
467
|
end
|
data/lib/halunke/tokenizer.rl
CHANGED
@@ -19,20 +19,20 @@
|
|
19
19
|
|
20
20
|
main := |*
|
21
21
|
|
22
|
-
number => { emit(:NUMBER, data[ts...te].to_r) };
|
23
|
-
string => { emit(:STRING, data[ts+1...te-1]) };
|
24
|
-
unassigned_bareword => { emit(:UNASSIGNED_BAREWORD, data[ts+1 ...te]) };
|
25
|
-
bareword => { emit(:BAREWORD, data[ts...te]) };
|
26
|
-
open_paren => { emit(:OPEN_PAREN, data[ts...te]) };
|
27
|
-
close_paren => { emit(:CLOSE_PAREN, data[ts...te]) };
|
28
|
-
open_curly => { emit(:OPEN_CURLY, data[ts...te]) };
|
29
|
-
close_curly => { emit(:CLOSE_CURLY, data[ts...te]) };
|
30
|
-
open_bracket => { emit(:OPEN_BRACKET, data[ts...te]) };
|
31
|
-
close_bracket => { emit(:CLOSE_BRACKET, data[ts...te]) };
|
32
|
-
open_dict_bracket => { emit(:OPEN_DICT_BRACKET, data[ts...te]) };
|
33
|
-
start_comment => { emit(:START_COMMENT, data[ts...te]) };
|
34
|
-
end_comment => { emit(:END_COMMENT, data[ts...te]) };
|
35
|
-
bar => { emit(:BAR, data[ts...te]) };
|
22
|
+
number => { emit(:NUMBER, data[ts...te].to_r, ts, te - 1) };
|
23
|
+
string => { emit(:STRING, data[ts+1...te-1], ts, te - 1) };
|
24
|
+
unassigned_bareword => { emit(:UNASSIGNED_BAREWORD, data[ts+1 ...te], ts, te - 1) };
|
25
|
+
bareword => { emit(:BAREWORD, data[ts...te], ts, te - 1) };
|
26
|
+
open_paren => { emit(:OPEN_PAREN, data[ts...te], ts, te - 1) };
|
27
|
+
close_paren => { emit(:CLOSE_PAREN, data[ts...te], ts, te - 1) };
|
28
|
+
open_curly => { emit(:OPEN_CURLY, data[ts...te], ts, te - 1) };
|
29
|
+
close_curly => { emit(:CLOSE_CURLY, data[ts...te], ts, te - 1) };
|
30
|
+
open_bracket => { emit(:OPEN_BRACKET, data[ts...te], ts, te - 1) };
|
31
|
+
close_bracket => { emit(:CLOSE_BRACKET, data[ts...te], ts, te - 1) };
|
32
|
+
open_dict_bracket => { emit(:OPEN_DICT_BRACKET, data[ts...te], ts, te - 1) };
|
33
|
+
start_comment => { emit(:START_COMMENT, data[ts...te], ts, te - 1) };
|
34
|
+
end_comment => { emit(:END_COMMENT, data[ts...te], ts, te - 1) };
|
35
|
+
bar => { emit(:BAR, data[ts...te], ts, te - 1) };
|
36
36
|
space;
|
37
37
|
any => { raise "Could not lex '#{ data[ts...te] }'" };
|
38
38
|
|
@@ -59,8 +59,8 @@ module Halunke
|
|
59
59
|
|
60
60
|
private
|
61
61
|
|
62
|
-
def emit(type, value)
|
63
|
-
@tokens << [ type, value ]
|
62
|
+
def emit(type, value, ts, te)
|
63
|
+
@tokens << [ type, [ value, ts, te ] ]
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
data/lib/halunke/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: halunke
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lucas Dohmen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 2.0.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: warning
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.10.1
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.10.1
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,6 +133,7 @@ files:
|
|
119
133
|
- docs/true-false.md
|
120
134
|
- docs/web.md
|
121
135
|
- examples/counter.hal
|
136
|
+
- examples/error.hal
|
122
137
|
- examples/hello.hal
|
123
138
|
- examples/stdio.hal
|
124
139
|
- examples/web.hal
|
@@ -126,6 +141,7 @@ files:
|
|
126
141
|
- halunke.gemspec
|
127
142
|
- lib/halunke.rb
|
128
143
|
- lib/halunke/grammar.y
|
144
|
+
- lib/halunke/herror.rb
|
129
145
|
- lib/halunke/interpreter.rb
|
130
146
|
- lib/halunke/nodes.rb
|
131
147
|
- lib/halunke/parser.rb
|
@@ -144,6 +160,7 @@ files:
|
|
144
160
|
- lib/halunke/runtime/hunassigned_bareword.rb
|
145
161
|
- lib/halunke/runtime/hweb.rb
|
146
162
|
- lib/halunke/runtime/true.hal
|
163
|
+
- lib/halunke/source_code_position.rb
|
147
164
|
- lib/halunke/tokenizer.rb
|
148
165
|
- lib/halunke/tokenizer.rl
|
149
166
|
- lib/halunke/version.rb
|