halunke 0.9.0 → 0.10.1

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.
data/docs/index.md CHANGED
@@ -70,10 +70,10 @@ the value is `["Ha" "Spe"]`.
70
70
  If you want to find out more about the types of objects in Halunke
71
71
  and the messages you can send to them, explore the navigation bar
72
72
  at the top. If you want to learn more about conditionals, check out
73
- the section about [True & False](/halunke/true-false). If you want
73
+ the section about [True & False](/true-false). If you want
74
74
  to write your own functions, check out
75
- [Function](/halunke/function) and if you want to define your own
76
- classes, check out [Class](/halunke/class).
75
+ [Function](/function) and if you want to define your own
76
+ classes, check out [Class](/class).
77
77
 
78
78
  If you want to store an object in a variable, you can do it like this:
79
79
 
@@ -87,7 +87,7 @@ with a value, and it will assign it. Be aware that you can't reassign. So if
87
87
  you assign something to a once, it will stay like this forever (within that
88
88
  scope). Reassigning will result in an error. *There will be reassignable
89
89
  references in the future, see [the issue for
90
- details](https://github.com/moonglum/halunke/issues/3)*
90
+ details](https://codeberg.org/moonglum/halunke/issues/3)*
91
91
 
92
92
  The values are also immutable. If you send a message to an object,
93
93
  it will not change the object, but it will return an answer to you.
data/docs/stdio.md CHANGED
@@ -4,7 +4,7 @@ title: Stdio
4
4
 
5
5
  The object `stdio` is available from everywhere. *This will change in the
6
6
  future. [Interacting with STDIO is IO and will be
7
- isolated.](https://github.com/moonglum/halunke/issues/4)*
7
+ isolated.](https://codeberg.org/moonglum/halunke/issues/4)*
8
8
 
9
9
  ## `puts`
10
10
 
data/docs/web.md CHANGED
@@ -4,7 +4,7 @@ title: Web
4
4
 
5
5
  The object `web` is available from everywhere. *This will change in the future.
6
6
  [Interacting with STDIO is IO and will be
7
- isolated.](https://github.com/moonglum/halunke/issues/4)*
7
+ isolated.](https://codeberg.org/moonglum/halunke/issues/4)*
8
8
 
9
9
  You can send the `run on` message to `web`. It expects the first object to be
10
10
  your application (more on that later) and the second one the bind address and
data/exe/halunke CHANGED
@@ -1,16 +1,21 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ if File.exist?(File.expand_path("../.git", __dir__))
4
+ lib = File.expand_path("../lib", __dir__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ end
7
+
3
8
  require "halunke/version"
4
9
  require "halunke/interpreter"
5
- require "readline"
10
+ require "reline"
6
11
 
7
12
  interpreter = Halunke::Interpreter.new
8
13
 
9
- Readline.completion_proc = ->(*) {[]}
14
+ Reline.completion_proc = ->(*) {[]}
10
15
 
11
16
  if ARGV.length == 0
12
17
  puts "Halunke #{Halunke::VERSION} REPL. Ctrl+d to quit"
13
- while (line = Readline.readline(">> ", true))
18
+ while (line = Reline.readline(">> ", true))
14
19
  begin
15
20
  value = interpreter.eval(line, error_mode: :repl)
16
21
  puts "=> #{value}" if value
data/halunke.gemspec CHANGED
@@ -21,11 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ["lib"]
23
23
 
24
- spec.add_dependency "rack", "~> 2.0.4"
25
-
26
- spec.add_development_dependency "warning", "~> 0.10.1"
27
- spec.add_development_dependency "bundler", "~> 1.17.2"
28
- spec.add_development_dependency "rake", "~> 12.3.2"
29
- spec.add_development_dependency "minitest", "~> 5.11.3"
30
- spec.add_development_dependency "racc", "~> 1.4.15"
24
+ spec.add_dependency "rackup", "~> 2.3"
25
+ spec.add_dependency "webrick"
26
+ spec.add_dependency "reline"
31
27
  end
@@ -27,15 +27,19 @@ rule
27
27
  | Expression Expressions { Nodes.new([val[0]].concat(val[1].nodes)) }
28
28
  ;
29
29
 
30
+ Arguments:
31
+ /* empty */ { ArrayNode.new([]) }
32
+ | BAR Expressions BAR { ArrayNode.new(val[1].nodes, val[0][1], val[2][2]) }
33
+ ;
34
+
30
35
  Expression:
31
36
  NUMBER { NumberNode.new(*val[0]) }
32
37
  | STRING { StringNode.new(*val[0]) }
33
38
  | BAREWORD { BarewordNode.new(*val[0]) }
34
39
  | UNASSIGNED_BAREWORD { UnassignedNode.new(BarewordNode.new(*val[0]), val[0][1], val[0][2]) }
35
40
  | START_COMMENT Expressions END_COMMENT { Nodes.new([]) }
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]) }
41
+ | OPEN_CURLY Arguments Expressions CLOSE_CURLY { FunctionNode.new(val[1], val[2], val[0][1], val[3][2]) }
42
+ | OPEN_PAREN Expressions CLOSE_PAREN { MessageSendNode.new(val[1].nodes, val[0][1], val[2][2]) }
39
43
  | OPEN_BRACKET Expressions CLOSE_BRACKET { ArrayNode.new(val[1].nodes, val[0][1], val[2][2]) }
40
44
  | OPEN_DICT_BRACKET Expressions CLOSE_BRACKET { DictionaryNode.new(val[1].nodes, val[0][1], val[2][2]) }
41
45
  ;
@@ -48,4 +48,13 @@ module Halunke
48
48
 
49
49
  class HUnknownAttribute < HError
50
50
  end
51
+
52
+ class HInvalidMessage < HError
53
+ end
54
+
55
+ class HInvalidDictionary < HError
56
+ end
57
+
58
+ class HInvalidMessageName < HError
59
+ end
51
60
  end
data/lib/halunke/nodes.rb CHANGED
@@ -11,31 +11,40 @@ module Halunke
11
11
 
12
12
  NumberNode = Struct.new(:value, :ts, :te) do
13
13
  def eval(context)
14
- context["Number"].create_instance(value)
14
+ context["Number"].create_instance(value,
15
+ source_code_position: source_code_position)
15
16
  end
16
17
 
17
18
  def ==(other)
18
19
  other.is_a?(NumberNode) &&
19
20
  value == other.value
20
21
  end
22
+
23
+ def source_code_position
24
+ SourceCodePosition.new(ts, te)
25
+ end
21
26
  end
22
27
 
23
28
  StringNode = Struct.new(:value, :ts, :te) do
24
29
  def eval(context)
25
- context["String"].create_instance(value)
30
+ context["String"].create_instance(value,
31
+ source_code_position: source_code_position)
26
32
  end
27
33
 
28
34
  def ==(other)
29
35
  other.is_a?(StringNode) &&
30
36
  value == other.value
31
37
  end
38
+
39
+ def source_code_position
40
+ SourceCodePosition.new(ts, te)
41
+ end
32
42
  end
33
43
 
34
44
  BarewordNode = Struct.new(:value, :ts, :te) do
35
45
  def eval(context)
36
46
  context[value]
37
47
  rescue KeyError
38
- source_code_position = SourceCodePosition.new(ts, te)
39
48
  raise HUnassignedBareword.new("Bareword '#{value} is unassigned", source_code_position)
40
49
  end
41
50
 
@@ -43,22 +52,30 @@ module Halunke
43
52
  other.is_a?(BarewordNode) &&
44
53
  value == other.value
45
54
  end
55
+
56
+ def source_code_position
57
+ SourceCodePosition.new(ts, te)
58
+ end
46
59
  end
47
60
 
48
61
  UnassignedNode = Struct.new(:node, :ts, :te) do
49
62
  def eval(context)
50
- context["UnassignedBareword"].create_instance(node.value, source_code_position: SourceCodePosition.new(ts, te))
63
+ context["UnassignedBareword"].create_instance(node.value, source_code_position: source_code_position)
51
64
  end
52
65
 
53
66
  def ==(other)
54
67
  other.is_a?(UnassignedNode) &&
55
68
  node == other.node
56
69
  end
70
+
71
+ def source_code_position
72
+ SourceCodePosition.new(ts, te)
73
+ end
57
74
  end
58
75
 
59
76
  FunctionNode = Struct.new(:params, :body, :ts, :te) do
60
77
  def eval(context)
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?
78
+ raise HEmptyFunction.new("This function would not return anything, in Halunke every function needs to return something.", source_code_position) if body.empty?
62
79
  signature = params.nodes.map(&:node).map(&:value)
63
80
 
64
81
  context["Function"].new(signature, lambda { |call_context|
@@ -71,6 +88,10 @@ module Halunke
71
88
  params == other.params &&
72
89
  body == other.body
73
90
  end
91
+
92
+ def source_code_position
93
+ SourceCodePosition.new(ts, te)
94
+ end
74
95
  end
75
96
 
76
97
  ArrayNode = Struct.new(:nodes, :ts, :te) do
@@ -82,56 +103,79 @@ module Halunke
82
103
  other.is_a?(ArrayNode) &&
83
104
  nodes == other.nodes
84
105
  end
106
+
107
+ def source_code_position
108
+ SourceCodePosition.new(ts, te)
109
+ end
85
110
  end
86
111
 
87
112
  DictionaryNode = Struct.new(:nodes, :ts, :te) do
88
113
  def eval(context)
89
114
  hash = {}
90
115
  nodes.each_slice(2) do |key, value|
116
+ raise HInvalidDictionary.new("This key has no value", key.source_code_position) if value.nil?
91
117
  hash[key.eval(context)] = value.eval(context)
92
118
  end
93
- context["Dictionary"].create_instance(hash)
119
+ context["Dictionary"].create_instance(hash,
120
+ source_code_position: source_code_position)
94
121
  end
95
122
 
96
123
  def ==(other)
97
124
  other.is_a?(DictionaryNode) &&
98
125
  nodes == other.nodes
99
126
  end
127
+
128
+ def source_code_position
129
+ SourceCodePosition.new(ts, te)
130
+ end
100
131
  end
101
132
 
102
- MessageSendNode = Struct.new(:receiver, :message, :ts, :te) do
133
+ MessageSendNode = Struct.new(:nodes, :ts, :te) do
103
134
  def eval(context)
104
- receiver.eval(context).receive_message(context, *message.eval(context),
105
- source_code_position: SourceCodePosition.new(ts, te))
135
+ message_name, message_value = parse_message(context)
136
+ receiver.eval(context).receive_message(context, message_name, message_value,
137
+ source_code_position: source_code_position)
106
138
  end
107
139
 
108
140
  def ==(other)
109
141
  other.is_a?(MessageSendNode) &&
110
- receiver == other.receiver &&
111
- message == other.message
142
+ nodes == other.nodes
112
143
  end
113
- end
114
144
 
115
- MessageNode = Struct.new(:nodes) do
116
- def eval(context)
117
- if nodes.length == 1
118
- if nodes[0].is_a? NumberNode
119
- # hack to allow expressions like (1+5)
120
- ["+", [nodes[0].eval(context)]]
121
- else
122
- [nodes[0].value, []]
123
- end
124
- elsif nodes.length.even?
125
- name = []
126
- message = []
127
- nodes.each_slice(2) do |name_part, value|
128
- name.push(name_part.value)
129
- message.push(value.eval(context))
130
- end
131
- [name.join(" "), message]
132
- else
133
- raise "Parse Error"
145
+ def source_code_position
146
+ SourceCodePosition.new(ts, te)
147
+ end
148
+
149
+ private
150
+
151
+ def receiver
152
+ raise HInvalidMessage.new("This message has no receiver", source_code_position) if nodes.length == 0
153
+ @receiver ||= nodes[0]
154
+ end
155
+
156
+ def parse_message(context)
157
+ if message_nodes.length == 1
158
+ # hack to allow expressions like (1+5)
159
+ return ["+", [message_nodes[0].eval(context)]] if message_nodes[0].is_a? NumberNode
160
+ return [message_nodes[0].value, []] if message_nodes[0].is_a? BarewordNode
161
+
162
+ raise HInvalidMessage.new("This is not a bareword", message_nodes[0].source_code_position)
163
+ end
164
+
165
+ name = []
166
+ message = []
167
+ message_nodes.each_slice(2) do |name_part, value|
168
+ raise HInvalidMessage.new("This is not a bareword", name_part.source_code_position) unless name_part.is_a? BarewordNode
169
+ raise HInvalidMessage.new("This bareword has no according argument", name_part.source_code_position) if value.nil?
170
+ name << name_part
171
+ message << value
134
172
  end
173
+ [name.map(&:value).join(" "), message.map { |node| node.eval(context) }]
174
+ end
175
+
176
+ def message_nodes
177
+ raise HInvalidMessage.new("You are sending an empty message", source_code_position) if nodes.length == 1
178
+ @message_nodes ||= nodes.drop(1)
135
179
  end
136
180
  end
137
181
  end