loxxy 0.1.15 → 0.2.02
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/.rubocop.yml +47 -11
- data/CHANGELOG.md +66 -0
- data/README.md +177 -90
- data/bin/loxxy +54 -6
- data/lib/loxxy.rb +1 -0
- data/lib/loxxy/ast/all_lox_nodes.rb +1 -0
- data/lib/loxxy/ast/ast_builder.rb +20 -1
- data/lib/loxxy/ast/ast_visitor.rb +7 -0
- data/lib/loxxy/ast/lox_class_stmt.rb +5 -1
- data/lib/loxxy/ast/lox_super_expr.rb +35 -0
- data/lib/loxxy/back_end/engine.rb +41 -13
- data/lib/loxxy/back_end/lox_class.rb +22 -4
- data/lib/loxxy/back_end/lox_function.rb +6 -0
- data/lib/loxxy/back_end/lox_instance.rb +0 -4
- data/lib/loxxy/back_end/resolver.rb +38 -2
- data/lib/loxxy/back_end/symbol_table.rb +1 -18
- data/lib/loxxy/cli_parser.rb +68 -0
- data/lib/loxxy/error.rb +3 -0
- data/lib/loxxy/front_end/grammar.rb +2 -2
- data/lib/loxxy/front_end/scanner.rb +32 -7
- data/lib/loxxy/interpreter.rb +12 -1
- data/lib/loxxy/version.rb +1 -1
- data/loxxy.gemspec +6 -2
- data/spec/back_end/engine_spec.rb +0 -8
- data/spec/front_end/scanner_spec.rb +34 -0
- data/spec/interpreter_spec.rb +94 -0
- metadata +9 -4
@@ -85,7 +85,7 @@ module Loxxy
|
|
85
85
|
name2envs[name] = [current_env]
|
86
86
|
end
|
87
87
|
|
88
|
-
anEntry.name
|
88
|
+
anEntry.name
|
89
89
|
end
|
90
90
|
|
91
91
|
# Search for the object with the given name
|
@@ -99,23 +99,6 @@ module Loxxy
|
|
99
99
|
sc.defns[aName]
|
100
100
|
end
|
101
101
|
|
102
|
-
# Search for the object with the given i_name
|
103
|
-
# @param anIName [String]
|
104
|
-
# @return [BackEnd::Variable]
|
105
|
-
# def lookup_i_name(anIName)
|
106
|
-
# found = nil
|
107
|
-
# environment = current_env
|
108
|
-
|
109
|
-
# begin
|
110
|
-
# found = environment.defns.values.find { |e| e.i_name == anIName }
|
111
|
-
# break if found
|
112
|
-
|
113
|
-
# environment = environment.parent
|
114
|
-
# end while environment
|
115
|
-
|
116
|
-
# found
|
117
|
-
# end
|
118
|
-
|
119
102
|
# Return all variables defined in the current .. root chain.
|
120
103
|
# Variables are sorted top-down and left-to-right.
|
121
104
|
def all_variables
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse' # Use standard OptionParser class for command-line parsing
|
4
|
+
|
5
|
+
module Loxxy
|
6
|
+
# A command-line option parser for the Loxxy interpreter.
|
7
|
+
# It is a specialisation of the OptionParser class.
|
8
|
+
class CLIParser < OptionParser
|
9
|
+
# @return [Hash{Symbol=>String, Array}]
|
10
|
+
attr_reader(:parsed_options)
|
11
|
+
|
12
|
+
# Constructor.
|
13
|
+
def initialize(prog_name, ver)
|
14
|
+
super()
|
15
|
+
reset(prog_name, ver)
|
16
|
+
|
17
|
+
heading
|
18
|
+
separator 'Options:'
|
19
|
+
separator ''
|
20
|
+
add_tail_options
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse!(args)
|
24
|
+
super
|
25
|
+
parsed_options
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def reset(prog_name, ver)
|
31
|
+
@program_name = prog_name
|
32
|
+
@version = ver
|
33
|
+
@banner = "Usage: #{prog_name} LOX_FILE [options]"
|
34
|
+
@parsed_options = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def description
|
38
|
+
<<-DESCR
|
39
|
+
Description:
|
40
|
+
loxxy is a Lox interpreter, it executes the Lox file(s) given in command-line.
|
41
|
+
More on Lox Language: https://craftinginterpreters.com/the-lox-language.html
|
42
|
+
|
43
|
+
Example:
|
44
|
+
#{program_name} hello.lox
|
45
|
+
DESCR
|
46
|
+
end
|
47
|
+
|
48
|
+
def heading
|
49
|
+
banner
|
50
|
+
separator ''
|
51
|
+
separator description
|
52
|
+
separator ''
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_tail_options
|
56
|
+
on_tail('--version', 'Display the program version then quit.') do
|
57
|
+
puts version
|
58
|
+
exit(0)
|
59
|
+
end
|
60
|
+
|
61
|
+
on_tail('-?', '-h', '--help', 'Display this help then quit.') do
|
62
|
+
puts help
|
63
|
+
exit(0)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end # class
|
67
|
+
end # module
|
68
|
+
# End of file
|
data/lib/loxxy/error.rb
CHANGED
@@ -7,6 +7,9 @@ module Loxxy
|
|
7
7
|
# Error occurring while Loxxy executes some invalid Lox code.
|
8
8
|
class RuntimeError < Error; end
|
9
9
|
|
10
|
+
# Error occurring while Loxxy scans invalid input.
|
11
|
+
class ScanError < Error; end
|
12
|
+
|
10
13
|
# Error occurring while Loxxy parses some invalid Lox code.
|
11
14
|
class SyntaxError < Error; end
|
12
15
|
end
|
@@ -38,7 +38,7 @@ module Loxxy
|
|
38
38
|
rule('declaration' => 'stmt')
|
39
39
|
|
40
40
|
rule('classDecl' => 'CLASS classNaming class_body').as 'class_decl'
|
41
|
-
rule('classNaming' => 'IDENTIFIER LESS IDENTIFIER')
|
41
|
+
rule('classNaming' => 'IDENTIFIER LESS IDENTIFIER').as 'class_subclassing'
|
42
42
|
rule('classNaming' => 'IDENTIFIER').as 'class_name'
|
43
43
|
rule('class_body' => 'LEFT_BRACE methods_opt RIGHT_BRACE').as 'class_body'
|
44
44
|
rule('methods_opt' => 'method_plus')
|
@@ -143,7 +143,7 @@ module Loxxy
|
|
143
143
|
rule('primary' => 'STRING').as 'literal_expr'
|
144
144
|
rule('primary' => 'IDENTIFIER').as 'variable_expr'
|
145
145
|
rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN').as 'grouping_expr'
|
146
|
-
rule('primary' => 'SUPER DOT IDENTIFIER')
|
146
|
+
rule('primary' => 'SUPER DOT IDENTIFIER').as 'super_expr'
|
147
147
|
|
148
148
|
# Utility rules
|
149
149
|
rule('function' => 'IDENTIFIER LEFT_PAREN params_opt RIGHT_PAREN block').as 'function'
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'strscan'
|
4
4
|
require 'rley'
|
5
|
+
require_relative '../error'
|
5
6
|
require_relative '../datatype/all_datatypes'
|
6
7
|
require_relative 'literal'
|
7
8
|
|
@@ -60,8 +61,6 @@ module Loxxy
|
|
60
61
|
PRINT RETURN SUPER THIS TRUE VAR WHILE
|
61
62
|
].map { |x| [x, x] }.to_h
|
62
63
|
|
63
|
-
class ScanError < StandardError; end
|
64
|
-
|
65
64
|
# Constructor. Initialize a tokenizer for Lox input.
|
66
65
|
# @param source [String] Lox text to tokenize.
|
67
66
|
def initialize(source = nil)
|
@@ -117,11 +116,14 @@ module Loxxy
|
|
117
116
|
keyw = @@keywords[lexeme.upcase]
|
118
117
|
tok_type = keyw || 'IDENTIFIER'
|
119
118
|
token = build_token(tok_type, lexeme)
|
119
|
+
elsif scanner.scan(/"(?:\\"|[^"])*\z/)
|
120
|
+
# Error: unterminated string...
|
121
|
+
col = scanner.pos - @line_start + 1
|
122
|
+
raise ScanError, "Error: [line #{lineno}:#{col}]: Unterminated string."
|
120
123
|
else # Unknown token
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
raise ScanError, "Unknown token #{erroneous} on line #{lineno}"
|
124
|
+
col = scanner.pos - @line_start + 1
|
125
|
+
_erroneous = curr_ch.nil? ? '' : scanner.scan(/./)
|
126
|
+
raise ScanError, "Error: [line #{lineno}:#{col}]: Unexpected character."
|
125
127
|
end
|
126
128
|
|
127
129
|
return token
|
@@ -156,7 +158,7 @@ module Loxxy
|
|
156
158
|
when 'NUMBER'
|
157
159
|
value = Datatype::Number.new(aLexeme)
|
158
160
|
when 'STRING'
|
159
|
-
value = Datatype::LXString.new(aLexeme)
|
161
|
+
value = Datatype::LXString.new(unescape_string(aLexeme))
|
160
162
|
when 'TRUE'
|
161
163
|
value = Datatype::True.instance
|
162
164
|
else
|
@@ -166,6 +168,29 @@ module Loxxy
|
|
166
168
|
return [value, symb]
|
167
169
|
end
|
168
170
|
|
171
|
+
# Replace any sequence sequence by their "real" value.
|
172
|
+
def unescape_string(aText)
|
173
|
+
result = +''
|
174
|
+
previous = nil
|
175
|
+
|
176
|
+
aText.each_char do |ch|
|
177
|
+
if previous
|
178
|
+
if ch == ?n
|
179
|
+
result << "\n"
|
180
|
+
else
|
181
|
+
result << ch
|
182
|
+
end
|
183
|
+
previous = nil
|
184
|
+
elsif ch == '\\'
|
185
|
+
previous = ?\
|
186
|
+
else
|
187
|
+
result << ch
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
result
|
192
|
+
end
|
193
|
+
|
169
194
|
# Skip non-significant whitespaces and comments.
|
170
195
|
# Advance the scanner until something significant is found.
|
171
196
|
def skip_intertoken_spaces
|
data/lib/loxxy/interpreter.rb
CHANGED
@@ -24,6 +24,15 @@ module Loxxy
|
|
24
24
|
# @param lox_input [String] Lox program to evaluate
|
25
25
|
# @return [Loxxy::Datatype::BuiltinDatatype]
|
26
26
|
def evaluate(lox_input)
|
27
|
+
raw_evaluate(lox_input).first
|
28
|
+
end
|
29
|
+
|
30
|
+
# Evaluate the given Lox program.
|
31
|
+
# Return the pair [result, a BackEnd::Engine instance]
|
32
|
+
# where result is the value of the last executed expression (if any)
|
33
|
+
# @param lox_input [String] Lox program to evaluate
|
34
|
+
# @return Loxxy::Datatype::BuiltinDatatype, Loxxy::BackEnd::Engine]
|
35
|
+
def raw_evaluate(lox_input)
|
27
36
|
# Front-end scans, parses the input and blurps an AST...
|
28
37
|
parser = FrontEnd::Parser.new
|
29
38
|
|
@@ -34,7 +43,9 @@ module Loxxy
|
|
34
43
|
# Back-end launches the tree walking & responds to visit events
|
35
44
|
# by executing the code determined by the visited AST node.
|
36
45
|
engine = BackEnd::Engine.new(config)
|
37
|
-
engine.execute(visitor)
|
46
|
+
result = engine.execute(visitor)
|
47
|
+
|
48
|
+
[result, engine]
|
38
49
|
end
|
39
50
|
end # class
|
40
51
|
end # module
|
data/lib/loxxy/version.rb
CHANGED
data/loxxy.gemspec
CHANGED
@@ -40,8 +40,12 @@ Gem::Specification.new do |spec|
|
|
40
40
|
spec.version = Loxxy::VERSION
|
41
41
|
spec.authors = ['Dimitri Geshef']
|
42
42
|
spec.email = ['famished.tiger@yahoo.com']
|
43
|
-
spec.summary = 'An implementation of the Lox programming language.
|
44
|
-
spec.description =
|
43
|
+
spec.summary = 'An implementation of the Lox programming language.'
|
44
|
+
spec.description = <<-DESCR_END
|
45
|
+
A Ruby implementation of the Lox programming language. Lox is a dynamically typed,
|
46
|
+
object-oriented programming language that features first-class functions, closures,
|
47
|
+
classes, and inheritance.
|
48
|
+
DESCR_END
|
45
49
|
spec.homepage = 'https://github.com/famished-tiger/loxxy'
|
46
50
|
spec.license = 'MIT'
|
47
51
|
spec.required_ruby_version = '~> 2.4'
|
@@ -34,15 +34,7 @@ module Loxxy
|
|
34
34
|
let(:var_decl) { Ast::LoxVarStmt.new(sample_pos, 'greeting', greeting) }
|
35
35
|
let(:lit_expr) { Ast::LoxLiteralExpr.new(sample_pos, greeting) }
|
36
36
|
|
37
|
-
it "should react to 'before_var_stmt' event" do
|
38
|
-
expect { subject.before_var_stmt(var_decl) }.not_to raise_error
|
39
|
-
current_env = subject.symbol_table.current_env
|
40
|
-
expect(current_env.defns['greeting']).to be_kind_of(Variable)
|
41
|
-
end
|
42
|
-
|
43
37
|
it "should react to 'after_var_stmt' event" do
|
44
|
-
# Precondition: `before_var_stmt` is called...
|
45
|
-
expect { subject.before_var_stmt(var_decl) }.not_to raise_error
|
46
38
|
# Precondition: value to assign is on top of stack
|
47
39
|
subject.stack.push(greeting)
|
48
40
|
|
@@ -189,6 +189,26 @@ LOX_END
|
|
189
189
|
end
|
190
190
|
end
|
191
191
|
|
192
|
+
it 'should recognize escaped quotes' do
|
193
|
+
embedded_quotes = %q{she said: \"Hello\"}
|
194
|
+
result = subject.send(:unescape_string, embedded_quotes)
|
195
|
+
expect(result).to eq('she said: "Hello"')
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'should recognize escaped backslash' do
|
199
|
+
embedded_backslash = 'backslash>\\\\'
|
200
|
+
result = subject.send(:unescape_string, embedded_backslash)
|
201
|
+
expect(result).to eq('backslash>\\')
|
202
|
+
end
|
203
|
+
|
204
|
+
# rubocop: disable Style/StringConcatenation
|
205
|
+
it 'should recognize newline escape sequence' do
|
206
|
+
embedded_newline = 'line1\\nline2'
|
207
|
+
result = subject.send(:unescape_string, embedded_newline)
|
208
|
+
expect(result).to eq('line1' + "\n" + 'line2')
|
209
|
+
end
|
210
|
+
# rubocop: enable Style/StringConcatenation
|
211
|
+
|
192
212
|
it 'should recognize a nil token' do
|
193
213
|
subject.start_with('nil')
|
194
214
|
token_nil = subject.tokens[0]
|
@@ -237,6 +257,20 @@ LOX_END
|
|
237
257
|
]
|
238
258
|
match_expectations(subject, expectations)
|
239
259
|
end
|
260
|
+
|
261
|
+
it 'should complain if it finds an unterminated string' do
|
262
|
+
subject.start_with('var a = "Unfinished;')
|
263
|
+
err = Loxxy::ScanError
|
264
|
+
err_msg = 'Error: [line 1:21]: Unterminated string.'
|
265
|
+
expect { subject.tokens }.to raise_error(err, err_msg)
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'should complain if it finds an unexpected character' do
|
269
|
+
subject.start_with('var a = ?1?;')
|
270
|
+
err = Loxxy::ScanError
|
271
|
+
err_msg = 'Error: [line 1:9]: Unexpected character.'
|
272
|
+
expect { subject.tokens }.to raise_error(err, err_msg)
|
273
|
+
end
|
240
274
|
end # context
|
241
275
|
end # describe
|
242
276
|
end # module
|
data/spec/interpreter_spec.rb
CHANGED
@@ -6,6 +6,7 @@ require 'stringio'
|
|
6
6
|
# Load the class under test
|
7
7
|
require_relative '../lib/loxxy/interpreter'
|
8
8
|
|
9
|
+
# rubocop: disable Metrics/ModuleLength
|
9
10
|
module Loxxy
|
10
11
|
# This spec contains the bare bones test for the Interpreter class.
|
11
12
|
# The execution of Lox code is tested elsewhere.
|
@@ -525,7 +526,100 @@ LOX_END
|
|
525
526
|
expect { subject.evaluate(program) }.not_to raise_error
|
526
527
|
expect(sample_cfg[:ostream].string).to eq('Egotist instance')
|
527
528
|
end
|
529
|
+
|
530
|
+
it 'should support a closure nested in a method' do
|
531
|
+
lox_snippet = <<-LOX_END
|
532
|
+
class Foo {
|
533
|
+
getClosure() {
|
534
|
+
fun closure() {
|
535
|
+
return this.toString();
|
536
|
+
}
|
537
|
+
return closure;
|
538
|
+
}
|
539
|
+
|
540
|
+
toString() { return "foo"; }
|
541
|
+
}
|
542
|
+
|
543
|
+
var closure = Foo().getClosure();
|
544
|
+
closure;
|
545
|
+
LOX_END
|
546
|
+
# Expected result: Backend::LoxFunction('closure')
|
547
|
+
# Expected function's closure (environment layout):
|
548
|
+
# Environment('global')
|
549
|
+
# defns
|
550
|
+
# +- ['clock'] => BackEnd::Engine::NativeFunction
|
551
|
+
# *- ['Foo'] => BackEnd::LoxClass
|
552
|
+
# Environment
|
553
|
+
# defns
|
554
|
+
# ['this'] => BackEnd::LoxInstance
|
555
|
+
# Environment
|
556
|
+
# defns
|
557
|
+
# +- ['closure'] => Backend::LoxFunction
|
558
|
+
result = subject.evaluate(lox_snippet)
|
559
|
+
expect(result).to be_kind_of(BackEnd::LoxFunction)
|
560
|
+
expect(result.name).to eq('closure')
|
561
|
+
closure = result.closure
|
562
|
+
expect(closure).to be_kind_of(Loxxy::BackEnd::Environment)
|
563
|
+
expect(closure.defns['closure'].value).to eq(result)
|
564
|
+
expect(closure.enclosing).to be_kind_of(Loxxy::BackEnd::Environment)
|
565
|
+
expect(closure.enclosing.defns['this'].value).to be_kind_of(Loxxy::BackEnd::LoxInstance)
|
566
|
+
global_env = closure.enclosing.enclosing
|
567
|
+
expect(global_env).to be_kind_of(Loxxy::BackEnd::Environment)
|
568
|
+
expect(global_env.defns['clock'].value).to be_kind_of(BackEnd::Engine::NativeFunction)
|
569
|
+
expect(global_env.defns['Foo'].value).to be_kind_of(BackEnd::LoxClass)
|
570
|
+
end
|
571
|
+
|
572
|
+
it 'should support custom initializer' do
|
573
|
+
lox_snippet = <<-LOX_END
|
574
|
+
// From section 3.9.5
|
575
|
+
class Breakfast {
|
576
|
+
init(meat, bread) {
|
577
|
+
this.meat = meat;
|
578
|
+
this.bread = bread;
|
579
|
+
}
|
580
|
+
|
581
|
+
serve(who) {
|
582
|
+
print "Enjoy your " + this.meat + " and " +
|
583
|
+
this.bread + ", " + who + ".";
|
584
|
+
}
|
585
|
+
}
|
586
|
+
|
587
|
+
var baconAndToast = Breakfast("bacon", "toast");
|
588
|
+
baconAndToast.serve("Dear Reader");
|
589
|
+
// Output: "Enjoy your bacon and toast, Dear Reader."
|
590
|
+
LOX_END
|
591
|
+
expect { subject.evaluate(lox_snippet) }.not_to raise_error
|
592
|
+
predicted = 'Enjoy your bacon and toast, Dear Reader.'
|
593
|
+
expect(sample_cfg[:ostream].string).to eq(predicted)
|
594
|
+
end
|
595
|
+
|
596
|
+
it 'should support class inheritance and super keyword' do
|
597
|
+
lox_snippet = <<-LOX_END
|
598
|
+
class A {
|
599
|
+
method() {
|
600
|
+
print "A method";
|
601
|
+
}
|
602
|
+
}
|
603
|
+
|
604
|
+
class B < A {
|
605
|
+
method() {
|
606
|
+
print "B method";
|
607
|
+
}
|
608
|
+
|
609
|
+
test() {
|
610
|
+
super.method();
|
611
|
+
}
|
612
|
+
}
|
613
|
+
|
614
|
+
class C < B {}
|
615
|
+
|
616
|
+
C().test();
|
617
|
+
LOX_END
|
618
|
+
expect { subject.evaluate(lox_snippet) }.not_to raise_error
|
619
|
+
expect(sample_cfg[:ostream].string).to eq('A method')
|
620
|
+
end
|
528
621
|
end # context
|
529
622
|
end # describe
|
530
623
|
# rubocop: enable Metrics/BlockLength
|
531
624
|
end # module
|
625
|
+
# rubocop: enable Metrics/ModuleLength
|
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.
|
4
|
+
version: 0.2.02
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-04-
|
11
|
+
date: 2021-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -66,7 +66,10 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3.0'
|
69
|
-
description:
|
69
|
+
description: |2
|
70
|
+
A Ruby implementation of the Lox programming language. Lox is a dynamically typed,
|
71
|
+
object-oriented programming language that features first-class functions, closures,
|
72
|
+
classes, and inheritance.
|
70
73
|
email:
|
71
74
|
- famished.tiger@yahoo.com
|
72
75
|
executables:
|
@@ -108,6 +111,7 @@ files:
|
|
108
111
|
- lib/loxxy/ast/lox_return_stmt.rb
|
109
112
|
- lib/loxxy/ast/lox_seq_decl.rb
|
110
113
|
- lib/loxxy/ast/lox_set_expr.rb
|
114
|
+
- lib/loxxy/ast/lox_super_expr.rb
|
111
115
|
- lib/loxxy/ast/lox_this_expr.rb
|
112
116
|
- lib/loxxy/ast/lox_unary_expr.rb
|
113
117
|
- lib/loxxy/ast/lox_var_stmt.rb
|
@@ -124,6 +128,7 @@ files:
|
|
124
128
|
- lib/loxxy/back_end/symbol_table.rb
|
125
129
|
- lib/loxxy/back_end/unary_operator.rb
|
126
130
|
- lib/loxxy/back_end/variable.rb
|
131
|
+
- lib/loxxy/cli_parser.rb
|
127
132
|
- lib/loxxy/datatype/all_datatypes.rb
|
128
133
|
- lib/loxxy/datatype/boolean.rb
|
129
134
|
- lib/loxxy/datatype/builtin_datatype.rb
|
@@ -178,7 +183,7 @@ requirements: []
|
|
178
183
|
rubygems_version: 3.1.4
|
179
184
|
signing_key:
|
180
185
|
specification_version: 4
|
181
|
-
summary: An implementation of the Lox programming language.
|
186
|
+
summary: An implementation of the Lox programming language.
|
182
187
|
test_files:
|
183
188
|
- spec/back_end/engine_spec.rb
|
184
189
|
- spec/back_end/environment_spec.rb
|