loxxy 0.4.07 → 0.4.09

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: 7cd3e438270c62a4863cddfd1b737182993ea205d84985e8862a25c74bb2e8c4
4
- data.tar.gz: f6bd428ebc505e238392cbb04e5fb1207eb34752f974ae8f81a23798e4ef51b5
3
+ metadata.gz: 8ece684c97fd1a382d69d7fb821bd33ca29b662c07230e1f305a9fb50e4abef1
4
+ data.tar.gz: 0400c4fe48329686f63d34693658c6a8960759ba10173958e0ef0c6b2b75880b
5
5
  SHA512:
6
- metadata.gz: 77ac1d09cb77b8afab98c21bb2f8544fd2961473efd1acaae3134a2aba0ed7138270aa86f384fe2ce1f76d865c8cd049dc3b8379b954c70992cc53b068244593
7
- data.tar.gz: 9fa0e0c3c3d96600bc4e8cd0447383a287be6ff483fb2a843056c87175239bc5fd319e0f11cfe8ab496652a773c91d91f8c05176275b07873f08ffad0de7a1f4
6
+ metadata.gz: 373db901360190ef0cd84fa3142ec7705922a41d77ec36e3a2f6b8a0b74fbc27c6f0e6de00fc8c38122813a4333758a497b9a88fc372c977a558c8722c19dccb
7
+ data.tar.gz: a52c9d016e7761ce6b0415cf7c5a409a90106e96c8c18dd8d7e264f87bf43a293d7314bf3854164e809cc1c9cb8354e4c9b97d5d1f2f57d6730e0ecf229e4502
data/.rubocop.yml CHANGED
@@ -3,7 +3,7 @@ AllCops:
3
3
  - 'exp/**/*'
4
4
  - 'demo/**/*'
5
5
 
6
- Gemspec/DateAssignment:
6
+ Gemspec/DeprecatedAttributeAssignment:
7
7
  Enabled: true
8
8
 
9
9
  Layout/ArgumentAlignment:
data/CHANGELOG.md CHANGED
@@ -1,4 +1,16 @@
1
- ## [0.4.07] - 2022-11-21
1
+ ## [0.4.09] - 2025-01-16
2
+ - Tested with MRI Ruby 3.4.1.
3
+ - Minor changes
4
+ - [NEW] Added `demo` folder
5
+ - [FIX] Some Rubocop 1.72.0 offences fixed.
6
+
7
+ ## [0.4.08] - 2022-04-09
8
+ - Refactoring of the `Scanner` class.
9
+
10
+ ### Changed
11
+ - `Frontend::Scanner` class: major code refactoring.
12
+
13
+ ## [0.4.07] - 2021-11-21
2
14
  - Minor fixes; dependency towards Rubies 3+ allowed...
3
15
 
4
16
  ### Fixed
@@ -8,7 +20,6 @@
8
20
  - File `duplicate_local.lox` Fixed typo in error message.
9
21
  - File `duplicate_parameter.lox` Fixed typo in error message.
10
22
 
11
-
12
23
  ## [0.4.06] - 2021-11-01
13
24
  - Code update to cope with `Rley` v.0.8.08 changes
14
25
 
data/README.md CHANGED
@@ -19,8 +19,8 @@ Although __Lox__ is fairly simple, it is far from being a toy language:
19
19
  ### Loxxy gem features
20
20
  - Complete tree-walking interpreter including lexer, parser and resolver
21
21
  - 100% pure Ruby with clean design (not a port from some other language)
22
- - Passes the `jox` (THE reference `Lox` implementation) test suite
23
- - Can run a Lox imterpreter implemented in ... `Lox` [LoxLox](https://github.com/benhoyt/loxlox),
22
+ - Passes the `jlox` (THE reference `Lox` implementation) test suite
23
+ - Can run a Lox interpreter implemented in ... `Lox` [LoxLox](https://github.com/benhoyt/loxlox),
24
24
  - Minimal runtime dependency (Rley gem). Won't drag a bunch of gems...
25
25
  - Ruby API for integrating a Lox interpreter with your code.
26
26
  - A command-line interpreter `loxxy`
@@ -55,7 +55,7 @@ And then execute:
55
55
  Create a text file and enter the following lines:
56
56
  ```javascript
57
57
  // hello.lox
58
- // Your firs Lox program
58
+ // Your first Lox program
59
59
  print "Hello, world.";
60
60
  ```
61
61
 
@@ -326,7 +326,9 @@ lox_input = <<-LOX_END
326
326
  print "Hello, world!";
327
327
  LOX_END
328
328
 
329
-
329
+ lox = Loxxy::Interpreter.new
330
+ lox.evaluate(lox_program) # => Hello, world!
331
+ ```
330
332
 
331
333
  ## Suppported Lox language features
332
334
 
@@ -188,7 +188,7 @@ module Loxxy
188
188
  # rule('varDecl' => 'VAR IDENTIFIER (EQUAL expression)? SEMICOLON')
189
189
  def reduce_var_declaration(_production, _range, tokens, theChildren)
190
190
  var_name = theChildren[1].token.lexeme.dup
191
- init_val = theChildren[2] ? theChildren[2].last : nil
191
+ init_val = theChildren[2]&.last
192
192
  Ast::LoxVarStmt.new(tokens[1].position, var_name, init_val)
193
193
  end
194
194
 
@@ -260,7 +260,7 @@ module Loxxy
260
260
 
261
261
  # rule('returnStmt' => 'RETURN expression? SEMICOLON')
262
262
  def reduce_return_stmt(_production, _range, tokens, theChildren)
263
- ret_expr = theChildren[1].nil? ? nil : theChildren[1].first
263
+ ret_expr = theChildren[1]&.first
264
264
  Ast::LoxReturnStmt.new(tokens[1].position, ret_expr)
265
265
  end
266
266
 
@@ -178,7 +178,6 @@ module Loxxy
178
178
  resolve_local(aThisExpr, aVisitor)
179
179
  end
180
180
 
181
- # rubocop: disable Style/CaseLikeIf
182
181
  # rubocop: disable Style/StringConcatenation
183
182
  def after_super_expr(aSuperExpr, aVisitor)
184
183
  msg_prefix = "Error at 'super': Can't use 'super' "
@@ -195,7 +194,6 @@ module Loxxy
195
194
  resolve_local(aSuperExpr, aVisitor)
196
195
  end
197
196
  # rubocop: enable Style/StringConcatenation
198
- # rubocop: enable Style/CaseLikeIf
199
197
 
200
198
  # function declaration creates a new scope for its body & binds its parameters for that scope
201
199
  def before_fun_stmt(aFunStmt, aVisitor)
@@ -63,7 +63,6 @@ module Loxxy
63
63
  # one Lox number and a Ruby Numeric
64
64
  # @param other [Loxxy::Datatype::Number, Numeric]
65
65
  # @return [Loxxy::Datatype::Number]
66
- # rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
67
66
  def /(other)
68
67
  case other
69
68
  when Number, Numeric
@@ -84,7 +83,6 @@ module Loxxy
84
83
  raise TypeError, err_msg
85
84
  end
86
85
  end
87
- # rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
88
86
 
89
87
  # Unary minus (return value with changed sign)
90
88
  # @return [Loxxy::Datatype::Number]
@@ -20,6 +20,15 @@ module Loxxy
20
20
  # Delimiters: e.g. parentheses '(', ')'
21
21
  # Separators: e.g. comma
22
22
  class Scanner
23
+ PATT_BLOCK_COMMENT_BEGIN = /\/\*/.freeze
24
+ PATT_BLOCK_COMMENT_END = /\*\//.freeze
25
+ PATT_COMPARISON = /[!=><]=?/.freeze
26
+ PATT_IDENTIFIER = /[a-zA-Z_][a-zA-Z_0-9]*/.freeze
27
+ PATT_LINE_COMMENT = /\/\/[^\r\n]*/.freeze
28
+ PATT_NEWLINE = /(?:\r\n)|\r|\n/.freeze
29
+ PATT_NUMBER = /\d+(?:\.\d+)?/.freeze
30
+ PATT_WHITESPACE = /[ \t\f]+/.freeze
31
+
23
32
  # @return [StringScanner] Low-level input scanner
24
33
  attr_reader(:scanner)
25
34
 
@@ -31,7 +40,7 @@ module Loxxy
31
40
 
32
41
  # One or two special character tokens.
33
42
  # These are enumerated in section 4.2.1 Token type
34
- @@lexeme2name = {
43
+ Lexeme2name = {
35
44
  '(' => 'LEFT_PAREN',
36
45
  ')' => 'RIGHT_PAREN',
37
46
  '{' => 'LEFT_BRACE',
@@ -58,7 +67,7 @@ module Loxxy
58
67
  @@keywords = %w[
59
68
  and class else false fun for if nil or
60
69
  print return super this true var while
61
- ].map { |x| [x, x] }.to_h
70
+ ].to_h { |x| [x, x] }
62
71
 
63
72
  # Single character that have a special meaning when escaped
64
73
  # @return [{Char => String}]
@@ -77,16 +86,16 @@ module Loxxy
77
86
  # Constructor. Initialize a tokenizer for Lox input.
78
87
  # @param source [String] Lox text to tokenize.
79
88
  def initialize(source = nil)
80
- @scanner = StringScanner.new('')
81
- start_with(source) if source
89
+ reset
90
+ input = source || ''
91
+ @scanner = StringScanner.new(input)
82
92
  end
83
93
 
84
94
  # Reset the tokenizer and make the given text, the current input.
85
95
  # @param source [String] Lox text to tokenize.
86
96
  def start_with(source)
97
+ reset
87
98
  @scanner.string = source
88
- @lineno = 1
89
- @line_start = 0
90
99
  end
91
100
 
92
101
  # Scan the source and return an array of tokens.
@@ -99,39 +108,79 @@ module Loxxy
99
108
  end
100
109
  tok_sequence << build_token('EOF', nil)
101
110
 
102
- return tok_sequence
111
+ tok_sequence
103
112
  end
104
113
 
105
114
  private
106
115
 
107
- def _next_token
108
- skip_intertoken_spaces
109
- curr_ch = scanner.peek(1)
110
- return nil if curr_ch.nil? || curr_ch.empty?
116
+ def reset
117
+ @state = :default
118
+ @lineno = 1
119
+ @line_start = 0
120
+ end
111
121
 
122
+ def _next_token
123
+ nesting_level = 0
112
124
  token = nil
113
125
 
114
- if '(){},.;+-/*'.include? curr_ch
115
- # Single delimiter or separator character
116
- token = build_token(@@lexeme2name[curr_ch], scanner.getch)
117
- elsif (lexeme = scanner.scan(/[!=><]=?/))
118
- # One or two special character tokens
119
- token = build_token(@@lexeme2name[lexeme], lexeme)
120
- elsif scanner.scan(/"/) # Start of string detected...
121
- token = build_string_token
122
- elsif (lexeme = scanner.scan(/\d+(?:\.\d+)?/))
123
- token = build_token('NUMBER', lexeme)
124
- elsif (lexeme = scanner.scan(/[a-zA-Z_][a-zA-Z_0-9]*/))
125
- keyw = @@keywords[lexeme]
126
- tok_type = keyw ? keyw.upcase : 'IDENTIFIER'
127
- token = build_token(tok_type, lexeme)
128
- else # Unknown token
129
- col = scanner.pos - @line_start + 1
130
- _erroneous = curr_ch.nil? ? '' : scanner.scan(/./)
131
- raise ScanError, "Error: [line #{lineno}:#{col}]: Unexpected character."
132
- end
126
+ # Loop until end of input reached or token found
127
+ until token || scanner.eos?
128
+ if scanner.skip(PATT_NEWLINE)
129
+ next_line_scanned
130
+ next
131
+ end
133
132
 
134
- return token
133
+ case @state
134
+ when :default
135
+ next if scanner.skip(PATT_WHITESPACE) # Skip whitespaces
136
+
137
+ curr_ch = scanner.peek(1)
138
+
139
+ token = if scanner.skip(PATT_LINE_COMMENT)
140
+ next
141
+ elsif scanner.skip(PATT_BLOCK_COMMENT_BEGIN)
142
+ @state = :in_block_comment
143
+ nesting_level = 1
144
+ next
145
+ elsif '(){},.;+-/*'.include? curr_ch
146
+ # Single delimiter or separator character
147
+ build_token(Lexeme2name[curr_ch], scanner.getch)
148
+ elsif (lexeme = scanner.scan(PATT_COMPARISON))
149
+ # One or two special character tokens
150
+ build_token(Lexeme2name[lexeme], lexeme)
151
+ elsif scanner.scan(/"/) # Start of string detected...
152
+ build_string_token
153
+ elsif (lexeme = scanner.scan(PATT_NUMBER))
154
+ build_token('NUMBER', lexeme)
155
+ elsif (lexeme = scanner.scan(PATT_IDENTIFIER))
156
+ keyw = @@keywords[lexeme]
157
+ tok_type = keyw ? keyw.upcase : 'IDENTIFIER'
158
+ build_token(tok_type, lexeme)
159
+ else # Unknown token
160
+ col = scanner.pos - @line_start + 1
161
+ _erroneous = curr_ch.nil? ? '' : scanner.scan(/./)
162
+ raise ScanError, "Error: [line #{lineno}:#{col}]: Unexpected character."
163
+ end
164
+
165
+ when :in_block_comment
166
+ comment_part = scanner.scan_until(/(?:\/\*)|(?:\*\/)|(?:(?:\r\n)|\r|\n)/)
167
+ unterminated_comment unless comment_part
168
+
169
+ case scanner.matched
170
+ when PATT_NEWLINE
171
+ next_line_scanned
172
+ when PATT_BLOCK_COMMENT_END
173
+ nesting_level -= 1
174
+ @state = :default if nesting_level.zero?
175
+ when PATT_BLOCK_COMMENT_BEGIN
176
+ nesting_level += 1
177
+ end
178
+ next
179
+ end # case
180
+ end # until
181
+
182
+ unterminated_comment unless nesting_level.zero?
183
+ token
135
184
  end
136
185
 
137
186
  def build_token(aSymbolName, aLexeme)
@@ -214,56 +263,12 @@ module Loxxy
214
263
  Rley::Lexical::Literal.new(lox_string, lexeme, 'STRING', pos)
215
264
  end
216
265
 
217
- # Skip non-significant whitespaces and comments.
218
- # Advance the scanner until something significant is found.
219
- def skip_intertoken_spaces
220
- loop do
221
- ws_found = scanner.skip(/[ \t\f]+/) ? true : false
222
- nl_found = scanner.skip(/(?:\r\n)|\r|\n/)
223
- if nl_found
224
- ws_found = true
225
- next_line
226
- end
227
- cmt_found = false
228
- if scanner.scan(/\/(\/|\*)/)
229
- cmt_found = true
230
- case scanner.matched
231
- when '//'
232
- scanner.skip(/[^\r\n]*(?:(?:\r\n)|\r|\n)?/)
233
- next_line
234
- when '/*'
235
- skip_block_comment
236
- next
237
- end
238
- end
239
- break unless ws_found || cmt_found
240
- end
241
-
242
- scanner.pos
243
- end
244
-
245
- def skip_block_comment
246
- nesting_level = 1
247
- loop do
248
- comment_part = scanner.scan_until(/(?:\/\*)|(?:\*\/)|(?:(?:\r\n)|\r|\n)/)
249
- unless comment_part
250
- msg = "Unterminated '/* ... */' block comment on line #{lineno}"
251
- raise ScanError, msg
252
- end
253
-
254
- case scanner.matched
255
- when /(?:(?:\r\n)|\r|\n)/
256
- next_line
257
- when '*/'
258
- nesting_level -= 1
259
- break if nesting_level.zero?
260
- when '/*'
261
- nesting_level += 1
262
- end
263
- end
266
+ def unterminated_comment
267
+ msg = "Unterminated '/* ... */' block comment on line #{lineno}"
268
+ raise ScanError, msg
264
269
  end
265
270
 
266
- def next_line
271
+ def next_line_scanned
267
272
  @lineno += 1
268
273
  @line_start = scanner.pos
269
274
  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.4.07'
4
+ VERSION = '0.4.09'
5
5
  end
data/loxxy.gemspec CHANGED
@@ -10,12 +10,10 @@ module PkgExtending
10
10
  file_list = Dir[
11
11
  '.rubocop.yml',
12
12
  '.rspec',
13
- '.travis.yml',
14
13
  '.yardopts',
15
14
  'Gemfile',
16
15
  'Rakefile',
17
16
  'CHANGELOG.md',
18
- 'CODE_OF_CONDUCT.md',
19
17
  'LICENSE.txt',
20
18
  'README.md',
21
19
  'loxxy.gemspec',
@@ -48,7 +46,7 @@ Gem::Specification.new do |spec|
48
46
  DESCR_END
49
47
  spec.homepage = 'https://github.com/famished-tiger/loxxy'
50
48
  spec.license = 'MIT'
51
- spec.required_ruby_version = '>= 2.5'
49
+ spec.required_ruby_version = '>= 2.6'
52
50
 
53
51
  spec.bindir = 'bin'
54
52
  spec.executables = ['loxxy']
@@ -58,10 +56,11 @@ Gem::Specification.new do |spec|
58
56
  PkgExtending.pkg_documentation(spec)
59
57
 
60
58
  # Runtime dependencies
61
- spec.add_dependency 'rley', '~> 0.8.08'
59
+ spec.add_dependency 'rley', '~> 0.8.10'
62
60
 
63
61
  # Development dependencies
64
- spec.add_development_dependency 'bundler', '~> 2.0'
65
- spec.add_development_dependency 'rake', '~> 12.0'
66
- spec.add_development_dependency 'rspec', '~> 3.0'
62
+ spec.add_development_dependency 'bundler', '~> 2.5.0'
63
+ spec.add_development_dependency 'rake', '~> 13.1.0'
64
+ spec.add_development_dependency 'rspec', '~> 3.12'
65
+ spec.add_development_dependency 'yard', '~> 0.9.34'
67
66
  end
@@ -15,7 +15,7 @@ module Loxxy
15
15
  subject { Engine.new(sample_options) }
16
16
 
17
17
  context 'Initialization:' do
18
- it 'should accept a option Hash at initialization' do
18
+ it 'should accept an option Hash at initialization' do
19
19
  expect { Engine.new(sample_options) }.not_to raise_error
20
20
  end
21
21
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loxxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.07
4
+ version: 0.4.09
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-01-21 00:00:00.000000000 Z
10
+ date: 2025-02-16 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rley
@@ -16,56 +15,70 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: 0.8.08
18
+ version: 0.8.10
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: 0.8.08
25
+ version: 0.8.10
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: bundler
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
- version: '2.0'
32
+ version: 2.5.0
34
33
  type: :development
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - "~>"
39
38
  - !ruby/object:Gem::Version
40
- version: '2.0'
39
+ version: 2.5.0
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: rake
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - "~>"
46
45
  - !ruby/object:Gem::Version
47
- version: '12.0'
46
+ version: 13.1.0
48
47
  type: :development
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
51
  - - "~>"
53
52
  - !ruby/object:Gem::Version
54
- version: '12.0'
53
+ version: 13.1.0
55
54
  - !ruby/object:Gem::Dependency
56
55
  name: rspec
57
56
  requirement: !ruby/object:Gem::Requirement
58
57
  requirements:
59
58
  - - "~>"
60
59
  - !ruby/object:Gem::Version
61
- version: '3.0'
60
+ version: '3.12'
62
61
  type: :development
63
62
  prerelease: false
64
63
  version_requirements: !ruby/object:Gem::Requirement
65
64
  requirements:
66
65
  - - "~>"
67
66
  - !ruby/object:Gem::Version
68
- version: '3.0'
67
+ version: '3.12'
68
+ - !ruby/object:Gem::Dependency
69
+ name: yard
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 0.9.34
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 0.9.34
69
82
  description: |2
70
83
  A Ruby implementation of the Lox programming language. Lox is a dynamically typed,
71
84
  object-oriented programming language that features first-class functions, closures,
@@ -80,7 +93,6 @@ extra_rdoc_files:
80
93
  files:
81
94
  - ".rspec"
82
95
  - ".rubocop.yml"
83
- - ".travis.yml"
84
96
  - ".yardopts"
85
97
  - CHANGELOG.md
86
98
  - Gemfile
@@ -163,7 +175,6 @@ homepage: https://github.com/famished-tiger/loxxy
163
175
  licenses:
164
176
  - MIT
165
177
  metadata: {}
166
- post_install_message:
167
178
  rdoc_options:
168
179
  - --charset=UTF-8 --exclude="examples|spec"
169
180
  require_paths:
@@ -172,15 +183,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
172
183
  requirements:
173
184
  - - ">="
174
185
  - !ruby/object:Gem::Version
175
- version: '2.5'
186
+ version: '2.6'
176
187
  required_rubygems_version: !ruby/object:Gem::Requirement
177
188
  requirements:
178
189
  - - ">="
179
190
  - !ruby/object:Gem::Version
180
191
  version: '0'
181
192
  requirements: []
182
- rubygems_version: 3.3.3
183
- signing_key:
193
+ rubygems_version: 3.6.2
184
194
  specification_version: 4
185
195
  summary: An implementation of the Lox programming language.
186
196
  test_files:
data/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- ---
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.6.3
6
- before_install: gem install bundler -v 2.1.4