frepl 0.0.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.
@@ -0,0 +1,46 @@
1
+ module Frepl
2
+ class ReplCommand < SinglelineStatement
3
+ COMMANDS = {
4
+ 'run' => {
5
+ info: 'Run the current set of Fortran code',
6
+ l: lambda { |file| file.run },
7
+ },
8
+ 'toggle_debug' => {
9
+ info: 'Toggle debug mode on/off',
10
+ l: lambda { |file| Frepl.debug = !Frepl.debug },
11
+ },
12
+ 'help' => {
13
+ info: 'View this hopefully helpful help',
14
+ l: lambda do |file|
15
+ puts "Available Frepl commands:\n"
16
+ COMMANDS.each do |k, v|
17
+ puts "f:#{k} -- #{v[:info]}"
18
+ end
19
+ end
20
+ },
21
+ 'z' => {
22
+ info: 'Undo last statement',
23
+ l: lambda do |file|
24
+ file.undo_last!
25
+ end
26
+ }
27
+ }
28
+
29
+ def accept(visitor)
30
+ visitor.visit_repl_command(self)
31
+ end
32
+
33
+ def run(file)
34
+ Frepl.log("running: #{cmd}")
35
+ if COMMANDS.key?(cmd)
36
+ COMMANDS[cmd][:l].call(file)
37
+ else
38
+ puts "Unknown command: `#{cmd}`. Type `f:help` for list of commands."
39
+ end
40
+ end
41
+
42
+ def cmd
43
+ @cmd ||= line.match(/f:(.+)/)[1]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ module Frepl
2
+ class StandaloneVariable < SinglelineStatement
3
+ def variable_name
4
+ @variable_name ||= @line.match(Frepl::Classifier::VARIABLE_NAME_REGEX).to_s
5
+ end
6
+
7
+ def accept(visitor)
8
+ visitor.visit_standalone_variable(self)
9
+ end
10
+
11
+ def expressionize
12
+ "write(*,*) #{variable_name}"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ module Frepl
2
+ class Subroutine < MultilineStatement
3
+ def terminal_regex
4
+ /end subroutine\s?#{Frepl::Classifier::VARIABLE_NAME_REGEX}/
5
+ end
6
+
7
+ def accept(visitor)
8
+ visitor.visit_subroutine(self)
9
+ end
10
+
11
+ def name
12
+ @name ||= lines.first.match(Frepl::Classifier::SUBROUTINE_REGEX)[1]
13
+ end
14
+
15
+ def ==(other)
16
+ if other.is_a?(Subroutine)
17
+ self.name == other.name
18
+ else
19
+ super(other)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def starting_regex
26
+ Frepl::Classifier::SUBROUTINE_REGEX
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ module Frepl
2
+ class Where < MultilineStatement
3
+ def terminal_regex
4
+ /end where/i
5
+ end
6
+
7
+ def accept(visitor)
8
+ visitor.visit_where(self)
9
+ end
10
+
11
+ private
12
+
13
+ def starting_regex
14
+ Frepl::Classifier::WHERE_REGEX
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Frepl
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Frepl::Assignment do
4
+ let(:a) { Frepl::Assignment.new('a = 2') }
5
+
6
+ describe '#variable_name' do
7
+ subject { a.variable_name }
8
+
9
+ it 'extracts variable name from line' do
10
+ expect(subject).to eql('a')
11
+ end
12
+
13
+ context 'with no spacing' do
14
+ let(:a) { Frepl::Assignment.new('a=2') }
15
+
16
+ it 'extracts variable name from line' do
17
+ expect(subject).to eql('a')
18
+ end
19
+ end
20
+
21
+ context 'with array' do
22
+ let(:a) { Frepl::Assignment.new('foobar = [1,2,3,4]') }
23
+
24
+ it 'extracts variable name from line' do
25
+ expect(subject).to eql('foobar')
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#assigned_value' do
31
+ subject { a.assigned_value }
32
+
33
+ it 'extracts value' do
34
+ expect(subject).to eql('2')
35
+ end
36
+
37
+ context 'with no spacing' do
38
+ let(:a) { Frepl::Assignment.new('a=2') }
39
+
40
+ it 'extracts variable name from line' do
41
+ expect(subject).to eql('2')
42
+ end
43
+ end
44
+
45
+ context 'with array' do
46
+ let(:a) { Frepl::Assignment.new('foobar = [1,2,3,4]') }
47
+
48
+ it 'extracts variable name from line' do
49
+ expect(subject).to eql('[1,2,3,4]')
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Frepl::Classifier do
4
+ let(:classifier) { Frepl::Classifier.new }
5
+ describe '#classify' do
6
+ context 'single declaration with assignment' do
7
+ let(:line) { 'integer a' }
8
+
9
+ it 'returns a single declaration' do
10
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
11
+ end
12
+ end
13
+
14
+ context 'single declaration with assignment' do
15
+ let(:line) { 'integer :: a = 1' }
16
+
17
+ it 'returns a single declaration' do
18
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
19
+ end
20
+ end
21
+
22
+ context 'single real kind declaration with assignment' do
23
+ let(:line) { 'real(kind=4) :: a = 1.0' }
24
+
25
+ it 'returns a single declaration' do
26
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
27
+ end
28
+ end
29
+
30
+ context 'parameter dp declaration' do
31
+ let(:line) { 'integer, parameter :: dp=kind(1.0d0)' }
32
+
33
+ it 'returns a single declaration' do
34
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
35
+ end
36
+ end
37
+
38
+ context 'real declaration using dp parameter' do
39
+ let(:line) { 'real(kind=dp) fk' }
40
+
41
+ it 'returns a single declaration' do
42
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
43
+ end
44
+ end
45
+
46
+ context 'single character declaration' do
47
+ let(:line) { 'character(len=4) name' }
48
+
49
+ it 'returns a single declaration' do
50
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
51
+ end
52
+ end
53
+
54
+ context 'single character declaration with assignment' do
55
+ let(:line) { 'character(len=4) :: name = "john"' }
56
+
57
+ it 'returns a single declaration' do
58
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
59
+ end
60
+ end
61
+
62
+ context 'single character declaration with assignment, without len' do
63
+ let(:line) { 'character(4) :: name = "john"' }
64
+
65
+ it 'returns a single declaration' do
66
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
67
+ end
68
+ end
69
+
70
+ context 'single Fortran 2003 array declaration' do
71
+ let(:line) { 'integer, dimension(3) :: a = [1,2,3]' }
72
+
73
+ it 'returns a single declaration' do
74
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
75
+ end
76
+ end
77
+
78
+ context 'single Fortran oldskool array declaration' do
79
+ let(:line) { 'integer, dimension(3) :: a = /1,2,3/' }
80
+
81
+ it 'returns a single declaration' do
82
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
83
+ end
84
+ end
85
+
86
+ context 'logical declaration' do
87
+ let(:line) { 'logical :: somecond = .TRUE.' }
88
+
89
+ it 'returns a single declaration' do
90
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
91
+ end
92
+ end
93
+
94
+ context 'multiple integer declaration' do
95
+ let(:line) { 'real :: a, b, c' }
96
+
97
+ it 'returns an multi declaration' do
98
+ expect(classifier.classify(line)).to be_a(Frepl::MultiDeclaration)
99
+ end
100
+ end
101
+
102
+ context 'multiple integer declaration with assignment' do
103
+ let(:line) { 'real :: a = 1, b, c=2' }
104
+
105
+ it 'returns an multi declaration' do
106
+ expect(classifier.classify(line)).to be_a(Frepl::MultiDeclaration)
107
+ end
108
+ end
109
+
110
+ context 'multiple array declaration' do
111
+ let(:line) { 'integer, dimension(3) :: a = [1,2,3], b = [1,2,3]' }
112
+
113
+ it 'returns a single declaration' do
114
+ expect(classifier.classify(line)).to be_a(Frepl::MultiDeclaration)
115
+ end
116
+ end
117
+
118
+ context 'lone variable name' do
119
+ let(:line) { 'foobar' }
120
+
121
+ it 'returns a standalone variable' do
122
+ expect(classifier.classify(line)).to be_a(Frepl::StandaloneVariable)
123
+ end
124
+ end
125
+
126
+ context 'start of if statement' do
127
+ let(:line) { 'if (9 < 10) then' }
128
+
129
+ it 'returns an IfStatement' do
130
+ expect(classifier.classify(line)).to be_a(Frepl::IfStatement)
131
+ end
132
+ end
133
+
134
+ context 'start of do loop' do
135
+ let(:line) { 'do i = 1, 3' }
136
+
137
+ it 'returns an DoLoop' do
138
+ expect(classifier.classify(line)).to be_a(Frepl::DoLoop)
139
+ end
140
+ end
141
+
142
+ context 'targetable array declaration' do
143
+ let(:line) { 'real, dimension(m,n), target :: A' }
144
+
145
+ it 'returns a single declaration' do
146
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
147
+ end
148
+ end
149
+
150
+ context 'pointer array declaration' do
151
+ let(:line) { 'real, dimension(:), pointer:: A' }
152
+
153
+ it 'returns a single declaration' do
154
+ expect(classifier.classify(line)).to be_a(Frepl::Declaration)
155
+ end
156
+ end
157
+ end
158
+
159
+ describe 'interruption' do
160
+ context 'in the middle of a multi-line statement' do
161
+ it 'should be able to be interrupted and start anew' do
162
+ classifier.classify('integer function add(a, b')
163
+ classifier.interrupt
164
+ classifier.classify('integer function sum(a, b')
165
+ classifier.classify('integer :: a, b')
166
+ classifier.classify('sum = a + b')
167
+ expect(classifier.classify('end function sum').complete?).to be true
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,193 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Frepl::Declaration do
4
+ context 'simple declaration' do
5
+ let(:d) { Frepl::Declaration.new('integer a') }
6
+
7
+ it 'extracts variable name' do
8
+ expect(d.variable_name).to eq('a')
9
+ end
10
+
11
+ it 'returns nil value' do
12
+ expect(d.assigned_value).to eq(nil)
13
+ end
14
+
15
+ it 'extracts type' do
16
+ expect(d.type).to eq('integer')
17
+ end
18
+ end
19
+
20
+ context 'simple, double-colon' do
21
+ let(:d) { Frepl::Declaration.new('real :: a') }
22
+
23
+ it 'extracts variable name' do
24
+ expect(d.variable_name).to eq('a')
25
+ end
26
+
27
+ it 'has nil assigned value' do
28
+ expect(d.assigned_value).to be_nil
29
+ end
30
+
31
+ it 'extracts type' do
32
+ expect(d.type).to eq('real')
33
+ end
34
+ end
35
+
36
+ context 'double-colon plus assignmentj' do
37
+ let(:d) { Frepl::Declaration.new('integer :: a = 3') }
38
+
39
+ it 'extracts variable name' do
40
+ expect(d.variable_name).to eq('a')
41
+ end
42
+
43
+ it 'has assigned value 3' do
44
+ expect(d.assigned_value).to eq('3')
45
+ end
46
+ end
47
+
48
+ context 'dimension plus assignment' do
49
+ let(:d) { Frepl::Declaration.new('integer, dimension(3) :: a = [1,2,3]') }
50
+
51
+ it 'extracts variable name' do
52
+ expect(d.variable_name).to eq('a')
53
+ end
54
+
55
+ it 'has assigned value [1,2,3]' do
56
+ expect(d.assigned_value).to eq('[1,2,3]')
57
+ end
58
+ end
59
+
60
+ context 'real with kind' do
61
+ let(:d) { Frepl::Declaration.new('real(kind=4) :: a = 2.3') }
62
+
63
+ it 'extracts variable name' do
64
+ expect(d.variable_name).to eq('a')
65
+ end
66
+
67
+ it 'has assigned value [1,2,3]' do
68
+ expect(d.assigned_value).to eq('2.3')
69
+ end
70
+
71
+ it 'has kind' do
72
+ expect(d.kind).to eq('4')
73
+ end
74
+ end
75
+
76
+ context 'real with kind' do
77
+ let(:d) { Frepl::Declaration.new('real(kind=4) :: a = 2.3') }
78
+
79
+ it 'extracts variable name' do
80
+ expect(d.variable_name).to eq('a')
81
+ end
82
+
83
+ it 'has assigned value [1,2,3]' do
84
+ expect(d.assigned_value).to eq('2.3')
85
+ end
86
+
87
+ it 'has kind' do
88
+ expect(d.kind).to eq('4')
89
+ end
90
+ end
91
+
92
+ context 'logical' do
93
+ let(:d) { Frepl::Declaration.new('logical :: cond = .FALSE.') }
94
+
95
+ it 'extracts variable name' do
96
+ expect(d.variable_name).to eq('cond')
97
+ end
98
+
99
+ it 'has assigned value .FALSE.' do
100
+ expect(d.assigned_value).to eq('.FALSE.')
101
+ end
102
+ end
103
+
104
+ context 'character' do
105
+ context 'with len' do
106
+ let(:c) { Frepl::Declaration.new('character(len=4) :: name = "luke"') }
107
+ it 'extracts variable name' do
108
+ expect(c.variable_name).to eq('name')
109
+ end
110
+
111
+ it 'has assigned value luke' do
112
+ expect(c.assigned_value).to eq('"luke"')
113
+ end
114
+
115
+ it 'has len' do
116
+ expect(c.len).to eq('4')
117
+ end
118
+ end
119
+
120
+ context 'without len' do
121
+ let(:c) { Frepl::Declaration.new('character(4) :: name = "luke"') }
122
+ it 'extracts variable name' do
123
+ expect(c.variable_name).to eq('name')
124
+ end
125
+
126
+ it 'has assigned value luke' do
127
+ expect(c.assigned_value).to eq('"luke"')
128
+ end
129
+
130
+ it 'has len' do
131
+ expect(c.len).to eq('4')
132
+ end
133
+ end
134
+
135
+ context 'assignments designed to trick regex' do
136
+ it 'works with equals' do
137
+ d = Frepl::Declaration.new('character(4) :: x = "\'="')
138
+ expect(d.assigned_value).to eq('"\'="')
139
+ expect(d.len).to eq('4')
140
+ end
141
+
142
+ it 'works with brackets' do
143
+ d = Frepl::Declaration.new('character(8) :: x = "]s\"')
144
+ expect(d.assigned_value).to eq('"]s\"')
145
+ expect(d.len).to eq('8')
146
+ end
147
+
148
+ it 'works with spaces' do
149
+ d = Frepl::Declaration.new('character(len=7) :: x = "len= f"')
150
+ expect(d.assigned_value).to eq('"len= f"')
151
+ expect(d.len).to eq('7')
152
+ end
153
+ end
154
+
155
+ context 'targetable array' do
156
+ let(:c) { Frepl::Declaration.new('real, dimension(m,n), target :: A') }
157
+
158
+ it 'extracts variable name' do
159
+ expect(c.variable_name).to eq('A')
160
+ end
161
+
162
+ it 'knows it is a pointer target' do
163
+ expect(c.target?).to be true
164
+ end
165
+ end
166
+
167
+ context 'pointer' do
168
+ let(:c) { Frepl::Declaration.new('real, dimension(:), pointer:: b') }
169
+
170
+ it 'extracts variable name' do
171
+ expect(c.variable_name).to eq('b')
172
+ end
173
+
174
+ it 'knows it is a pointer' do
175
+ expect(c.pointer?).to be true
176
+ end
177
+ end
178
+ end
179
+
180
+ describe '#==' do
181
+ let(:d) { Frepl::Declaration.new('integer a') }
182
+
183
+ context 'when other is a `Declaration`' do
184
+ let(:other_same) { Frepl::Declaration.new('real a') }
185
+ let(:other_diff) { Frepl::Declaration.new('integer b') }
186
+
187
+ it 'compares by variable name' do
188
+ expect(d).to eq(other_same)
189
+ expect(d).not_to eq(other_diff)
190
+ end
191
+ end
192
+ end
193
+ end