loxxy 0.1.02 → 0.1.03
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/CHANGELOG.md +15 -1
- data/README.md +2 -1
- data/bin/loxxy +11 -0
- data/lib/loxxy.rb +0 -2
- data/lib/loxxy/back_end/binary_operator.rb +57 -0
- data/lib/loxxy/back_end/engine.rb +60 -3
- data/lib/loxxy/back_end/unary_operator.rb +41 -0
- data/lib/loxxy/error.rb +9 -0
- data/lib/loxxy/version.rb +1 -1
- data/loxxy.gemspec +2 -2
- data/spec/interpreter_spec.rb +9 -0
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2bf956c6e2d6736535acd1e9b0cb29df6b55a8d9002be073cb70bbf753fb06c9
|
4
|
+
data.tar.gz: 1f635dc69e588f01406cd988bf98d12b9ea398f36e249113a37b089788ef21cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dce93451a9efc548df45683f5772a5d5509c25a57969465f3ff9f48f8b5adcc642b7bb92b25d43658869b5b62d3108796964226c35130cb1492335dc6eaf8fe1
|
7
|
+
data.tar.gz: 881dfd881db0d2ec3e56b91ad14bc6dd0e0b91ba3839fa1efe09c3636aac575856e3e8c4d4a64a55e913a757a4f1d275cc843734ceda3a23ef597591cb282f3f
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,22 @@
|
|
1
|
+
## [0.1.03] - 2021-02-26
|
2
|
+
- Runtime argument chacking for arithmetic and comparison operators
|
3
|
+
|
4
|
+
### Added
|
5
|
+
- Test suite for arithmetic and comparison operators (in project repository)
|
6
|
+
- Class `BackEnd::UnaryOperator`: runtime argument validation
|
7
|
+
- Class `BackEnd::BinaryOperator`: runtime argument validation
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- File `console` renamed to `loxxy`. Very basic command-line interface.
|
11
|
+
- Custom exception classes
|
12
|
+
- File `README.md` updated list of supported `Lox` keywords.
|
13
|
+
|
14
|
+
|
1
15
|
## [0.1.02] - 2021-02-21
|
2
16
|
- Function definition and call documented in `README.md`
|
3
17
|
|
4
18
|
### Changed
|
5
|
-
- File `README.md` updated
|
19
|
+
- File `README.md` updated description of function definition and function call.
|
6
20
|
|
7
21
|
### Fixed
|
8
22
|
- Method `BackEnd::Engine#after_print_stmt` now handles of empty stack or nil data.
|
data/README.md
CHANGED
@@ -180,7 +180,8 @@ Loxxy supports single line C-style comments.
|
|
180
180
|
### Keywords
|
181
181
|
Loxxy implements the following __Lox__ reserved keywords:
|
182
182
|
```lang-none
|
183
|
-
and, false,
|
183
|
+
and, else, false, for, fun, if,
|
184
|
+
nil, or, print, true, var, while
|
184
185
|
```
|
185
186
|
|
186
187
|
### Datatypes
|
data/bin/loxxy
ADDED
data/lib/loxxy.rb
CHANGED
@@ -6,8 +6,6 @@ require_relative 'loxxy/front_end/raw_parser'
|
|
6
6
|
|
7
7
|
# Namespace for all classes and constants of __loxxy__ gem.
|
8
8
|
module Loxxy
|
9
|
-
class Error < StandardError; end
|
10
|
-
|
11
9
|
# Shorthand method. Returns the sole object that represents
|
12
10
|
# a Lox false literal.
|
13
11
|
# @return [Loxxy::Datatype::False]
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../error'
|
4
|
+
|
5
|
+
module Loxxy
|
6
|
+
module BackEnd
|
7
|
+
Signature = Struct.new(:parameter_types)
|
8
|
+
|
9
|
+
# A Lox binary operator
|
10
|
+
class BinaryOperator
|
11
|
+
# @return [String] text representation of the operator
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
# @return [Array<Class>]
|
15
|
+
attr_reader :signatures
|
16
|
+
|
17
|
+
# @param aName [String] "name" of operator
|
18
|
+
# @param theSignatures [Array<Signature>] allowed signatures
|
19
|
+
def initialize(aName, theSignatures)
|
20
|
+
@name = aName
|
21
|
+
@signatures = theSignatures
|
22
|
+
end
|
23
|
+
|
24
|
+
# rubocop: disable Style/ClassEqualityComparison
|
25
|
+
def validate_operands(operand1, operand2)
|
26
|
+
compliant = signatures.find do |(type1, type2)|
|
27
|
+
next unless operand1.kind_of?(type1)
|
28
|
+
|
29
|
+
if type2 == :idem
|
30
|
+
(operand2.class == operand1.class)
|
31
|
+
else
|
32
|
+
operand2.kind_of?(type2)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
# rubocop: enable Style/ClassEqualityComparison
|
36
|
+
|
37
|
+
unless compliant
|
38
|
+
err = Loxxy::RuntimeError
|
39
|
+
if signatures.size == 1 && signatures[0].last == :idem
|
40
|
+
raise err, "Operands must be #{datatype_name(signatures[0].first)}s."
|
41
|
+
elsif signatures.size == 2 && signatures.all? { |(_, second)| second == :idem }
|
42
|
+
type1 = datatype_name(signatures[0].first)
|
43
|
+
type2 = datatype_name(signatures[1].first)
|
44
|
+
raise err, "Operands must be two #{type1}s or two #{type2}s."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def datatype_name(aClass)
|
52
|
+
# (?:(?:[^:](?!:|(?<=LX))))+$
|
53
|
+
aClass.name.sub(/^.+(?=::)::(?:LX)?/, '').downcase
|
54
|
+
end
|
55
|
+
end # class
|
56
|
+
end # module
|
57
|
+
end # module
|
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
# Load all the classes implementing AST nodes
|
4
4
|
require_relative '../ast/all_lox_nodes'
|
5
|
+
require_relative 'binary_operator'
|
5
6
|
require_relative 'function'
|
6
7
|
require_relative 'symbol_table'
|
8
|
+
require_relative 'unary_operator'
|
7
9
|
|
8
10
|
module Loxxy
|
9
11
|
module BackEnd
|
@@ -20,13 +22,23 @@ module Loxxy
|
|
20
22
|
# @return [Array<Datatype::BuiltinDatatyp>] Stack for the values of expr
|
21
23
|
attr_reader :stack
|
22
24
|
|
25
|
+
# @return [Hash { Symbol => UnaryOperator}]
|
26
|
+
attr_reader :unary_operators
|
27
|
+
|
28
|
+
# @return [Hash { Symbol => BinaryOperator}]
|
29
|
+
attr_reader :binary_operators
|
30
|
+
|
23
31
|
# @param theOptions [Hash]
|
24
32
|
def initialize(theOptions)
|
25
33
|
@config = theOptions
|
26
34
|
@ostream = config.include?(:ostream) ? config[:ostream] : $stdout
|
27
35
|
@symbol_table = SymbolTable.new
|
28
36
|
@stack = []
|
37
|
+
@unary_operators = {}
|
38
|
+
@binary_operators = {}
|
29
39
|
|
40
|
+
init_unary_operators
|
41
|
+
init_binary_operators
|
30
42
|
init_globals
|
31
43
|
end
|
32
44
|
|
@@ -148,9 +160,11 @@ module Loxxy
|
|
148
160
|
end
|
149
161
|
|
150
162
|
def after_binary_expr(aBinaryExpr)
|
151
|
-
op = aBinaryExpr.operator
|
152
163
|
operand2 = stack.pop
|
153
164
|
operand1 = stack.pop
|
165
|
+
op = aBinaryExpr.operator
|
166
|
+
operator = binary_operators[op]
|
167
|
+
operator.validate_operands(operand1, operand2)
|
154
168
|
if operand1.respond_to?(op)
|
155
169
|
stack.push operand1.send(op, operand2)
|
156
170
|
else
|
@@ -160,12 +174,14 @@ module Loxxy
|
|
160
174
|
end
|
161
175
|
|
162
176
|
def after_unary_expr(anUnaryExpr)
|
163
|
-
op = anUnaryExpr.operator
|
164
177
|
operand = stack.pop
|
178
|
+
op = anUnaryExpr.operator
|
179
|
+
operator = unary_operators[op]
|
180
|
+
operator.validate_operand(operand)
|
165
181
|
if operand.respond_to?(op)
|
166
182
|
stack.push operand.send(op)
|
167
183
|
else
|
168
|
-
msg1 = "`#{op}': Unimplemented operator for a #{
|
184
|
+
msg1 = "`#{op}': Unimplemented operator for a #{operand.class}."
|
169
185
|
raise StandardError, msg1
|
170
186
|
end
|
171
187
|
end
|
@@ -235,6 +251,47 @@ module Loxxy
|
|
235
251
|
end
|
236
252
|
end
|
237
253
|
|
254
|
+
def init_unary_operators
|
255
|
+
negate_op = UnaryOperator.new('-', [Datatype::Number])
|
256
|
+
unary_operators[:-@] = negate_op
|
257
|
+
|
258
|
+
negation_op = UnaryOperator.new('!', [Datatype::BuiltinDatatype])
|
259
|
+
unary_operators[:!] = negation_op
|
260
|
+
end
|
261
|
+
|
262
|
+
def init_binary_operators
|
263
|
+
plus_op = BinaryOperator.new('+', [[Datatype::Number, :idem],
|
264
|
+
[Datatype::LXString, :idem]])
|
265
|
+
binary_operators[:+] = plus_op
|
266
|
+
|
267
|
+
minus_op = BinaryOperator.new('-', [[Datatype::Number, :idem]])
|
268
|
+
binary_operators[:-] = minus_op
|
269
|
+
|
270
|
+
star_op = BinaryOperator.new('*', [[Datatype::Number, :idem]])
|
271
|
+
binary_operators[:*] = star_op
|
272
|
+
|
273
|
+
slash_op = BinaryOperator.new('/', [[Datatype::Number, :idem]])
|
274
|
+
binary_operators[:/] = slash_op
|
275
|
+
|
276
|
+
equal_equal_op = BinaryOperator.new('==', [[Datatype::BuiltinDatatype, Datatype::BuiltinDatatype]])
|
277
|
+
binary_operators[:==] = equal_equal_op
|
278
|
+
|
279
|
+
not_equal_op = BinaryOperator.new('!=', [[Datatype::BuiltinDatatype, Datatype::BuiltinDatatype]])
|
280
|
+
binary_operators[:!=] = not_equal_op
|
281
|
+
|
282
|
+
less_op = BinaryOperator.new('<', [[Datatype::Number, :idem]])
|
283
|
+
binary_operators[:<] = less_op
|
284
|
+
|
285
|
+
less_equal_op = BinaryOperator.new('<=', [[Datatype::Number, :idem]])
|
286
|
+
binary_operators[:<=] = less_equal_op
|
287
|
+
|
288
|
+
greater_op = BinaryOperator.new('>', [[Datatype::Number, :idem]])
|
289
|
+
binary_operators[:>] = greater_op
|
290
|
+
|
291
|
+
greater_equal_op = BinaryOperator.new('>=', [[Datatype::Number, :idem]])
|
292
|
+
binary_operators[:>=] = greater_equal_op
|
293
|
+
end
|
294
|
+
|
238
295
|
def init_globals
|
239
296
|
add_native_fun('clock', native_clock)
|
240
297
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../error'
|
4
|
+
|
5
|
+
module Loxxy
|
6
|
+
module BackEnd
|
7
|
+
# A Lox unary operator
|
8
|
+
class UnaryOperator
|
9
|
+
# @return [String] text representation of the operator
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @return [Array<Class>]
|
13
|
+
attr_reader :signatures
|
14
|
+
|
15
|
+
# @param aName [String] "name" of operator
|
16
|
+
# @param theSignatures [Array<Class>] allowed signatures
|
17
|
+
def initialize(aName, theSignatures)
|
18
|
+
@name = aName
|
19
|
+
@signatures = theSignatures
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate_operand(operand1)
|
23
|
+
compliant = signatures.find { |some_type| operand1.kind_of?(some_type) }
|
24
|
+
|
25
|
+
unless compliant
|
26
|
+
err = Loxxy::RuntimeError
|
27
|
+
# if signatures.size == 1
|
28
|
+
raise err, "Operand must be a #{datatype_name(signatures[0])}."
|
29
|
+
# end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def datatype_name(aClass)
|
36
|
+
# (?:(?:[^:](?!:|(?<=LX))))+$
|
37
|
+
aClass.name.sub(/^.+(?=::)::(?:LX)?/, '').downcase
|
38
|
+
end
|
39
|
+
end # class
|
40
|
+
end # module
|
41
|
+
end # module
|
data/lib/loxxy/error.rb
ADDED
data/lib/loxxy/version.rb
CHANGED
data/loxxy.gemspec
CHANGED
@@ -46,8 +46,8 @@ Gem::Specification.new do |spec|
|
|
46
46
|
spec.license = 'MIT'
|
47
47
|
spec.required_ruby_version = '~> 2.4'
|
48
48
|
|
49
|
-
spec.bindir = '
|
50
|
-
spec.executables =
|
49
|
+
spec.bindir = 'bin'
|
50
|
+
spec.executables = ['loxxy']
|
51
51
|
spec.require_paths = ['lib']
|
52
52
|
|
53
53
|
PkgExtending.pkg_files(spec)
|
data/spec/interpreter_spec.rb
CHANGED
@@ -429,6 +429,15 @@ LOX_END
|
|
429
429
|
expect(sample_cfg[:ostream].string).to eq('Hello, world!')
|
430
430
|
end
|
431
431
|
end # context
|
432
|
+
|
433
|
+
context 'Test suite:' do
|
434
|
+
it "should complain if one argument isn't a number" do
|
435
|
+
source = '1 + nil;'
|
436
|
+
err = Loxxy::RuntimeError
|
437
|
+
err_msg = 'Operands must be two numbers or two strings.'
|
438
|
+
expect { subject.evaluate(source) }.to raise_error(err, err_msg)
|
439
|
+
end
|
440
|
+
end # context
|
432
441
|
end # describe
|
433
442
|
# rubocop: enable Metrics/BlockLength
|
434
443
|
end # module
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: loxxy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.03
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-02-
|
11
|
+
date: 2021-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -69,7 +69,8 @@ dependencies:
|
|
69
69
|
description: An implementation of the Lox programming language. WIP
|
70
70
|
email:
|
71
71
|
- famished.tiger@yahoo.com
|
72
|
-
executables:
|
72
|
+
executables:
|
73
|
+
- loxxy
|
73
74
|
extensions: []
|
74
75
|
extra_rdoc_files:
|
75
76
|
- README.md
|
@@ -83,6 +84,7 @@ files:
|
|
83
84
|
- LICENSE.txt
|
84
85
|
- README.md
|
85
86
|
- Rakefile
|
87
|
+
- bin/loxxy
|
86
88
|
- lib/loxxy.rb
|
87
89
|
- lib/loxxy/ast/all_lox_nodes.rb
|
88
90
|
- lib/loxxy/ast/ast_builder.rb
|
@@ -106,11 +108,13 @@ files:
|
|
106
108
|
- lib/loxxy/ast/lox_var_stmt.rb
|
107
109
|
- lib/loxxy/ast/lox_variable_expr.rb
|
108
110
|
- lib/loxxy/ast/lox_while_stmt.rb
|
111
|
+
- lib/loxxy/back_end/binary_operator.rb
|
109
112
|
- lib/loxxy/back_end/engine.rb
|
110
113
|
- lib/loxxy/back_end/entry.rb
|
111
114
|
- lib/loxxy/back_end/environment.rb
|
112
115
|
- lib/loxxy/back_end/function.rb
|
113
116
|
- lib/loxxy/back_end/symbol_table.rb
|
117
|
+
- lib/loxxy/back_end/unary_operator.rb
|
114
118
|
- lib/loxxy/back_end/variable.rb
|
115
119
|
- lib/loxxy/datatype/all_datatypes.rb
|
116
120
|
- lib/loxxy/datatype/boolean.rb
|
@@ -120,6 +124,7 @@ files:
|
|
120
124
|
- lib/loxxy/datatype/nil.rb
|
121
125
|
- lib/loxxy/datatype/number.rb
|
122
126
|
- lib/loxxy/datatype/true.rb
|
127
|
+
- lib/loxxy/error.rb
|
123
128
|
- lib/loxxy/front_end/grammar.rb
|
124
129
|
- lib/loxxy/front_end/literal.rb
|
125
130
|
- lib/loxxy/front_end/parser.rb
|