halunke 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|