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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ddfc01e822c7e68c87d515649f6ef6e2c800c926ca289dfe9f65edeff24e7015
4
- data.tar.gz: db5a6a8052c0c15f00920c6d76fcd8c6f9cfe380905c20b53f8e74cc7c6d4f84
3
+ metadata.gz: 2bf956c6e2d6736535acd1e9b0cb29df6b55a8d9002be073cb70bbf753fb06c9
4
+ data.tar.gz: 1f635dc69e588f01406cd988bf98d12b9ea398f36e249113a37b089788ef21cb
5
5
  SHA512:
6
- metadata.gz: f5a85f8a0a4a762f43a9dd51ab972f924a95ed15ed9e864cef0c60f4815902cbfb905fef624cc100bae9283985f8f6c7f960fbd72bd8d87e9ee3ab211eb7dc24
7
- data.tar.gz: 10522ab655b99e31007dccaa479b4ec59a9340160f6768c084c3b96825f4e89f9f1c46c1655117763d66f28004f5d8e2c193315c80a65dd49b733587bd25823a
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 todescribe function definition and function call.
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, nil, or, print, true
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
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'loxxy'
5
+
6
+ if ARGV[0]
7
+ lox = Loxxy::Interpreter.new
8
+ File.open(ARGV[0], 'r') do |f|
9
+ lox.evaluate(f.read)
10
+ end
11
+ end
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 #{operand1.class}."
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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Loxxy
4
+ # Abstract class. Generalization of Loxxy error classes.
5
+ class Error < StandardError; end
6
+
7
+ # Error occurring while Loxxy executes some invalid Lox code.
8
+ class RuntimeError < Error; end
9
+ end
data/lib/loxxy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.1.02'
4
+ VERSION = '0.1.03'
5
5
  end
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 = 'exe'
50
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
49
+ spec.bindir = 'bin'
50
+ spec.executables = ['loxxy']
51
51
  spec.require_paths = ['lib']
52
52
 
53
53
  PkgExtending.pkg_files(spec)
@@ -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.02
4
+ version: 0.1.03
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-21 00:00:00.000000000 Z
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