frepl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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