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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +120 -0
- data/Rakefile +15 -0
- data/bin/frepl +5 -0
- data/frepl.gemspec +29 -0
- data/lib/frepl.rb +81 -0
- data/lib/frepl/classifier.rb +190 -0
- data/lib/frepl/fortran_file.rb +159 -0
- data/lib/frepl/statement.rb +93 -0
- data/lib/frepl/statements/allocation.rb +9 -0
- data/lib/frepl/statements/assignment.rb +19 -0
- data/lib/frepl/statements/declaration.rb +47 -0
- data/lib/frepl/statements/derived_type.rb +29 -0
- data/lib/frepl/statements/do_loop.rb +17 -0
- data/lib/frepl/statements/execution.rb +7 -0
- data/lib/frepl/statements/function.rb +29 -0
- data/lib/frepl/statements/if_statement.rb +17 -0
- data/lib/frepl/statements/multi_declaration.rb +70 -0
- data/lib/frepl/statements/repl_command.rb +46 -0
- data/lib/frepl/statements/standalone_variable.rb +15 -0
- data/lib/frepl/statements/subroutine.rb +29 -0
- data/lib/frepl/statements/where.rb +17 -0
- data/lib/frepl/version.rb +3 -0
- data/spec/lib/frepl/assignment_spec.rb +53 -0
- data/spec/lib/frepl/classifier_spec.rb +171 -0
- data/spec/lib/frepl/declaration_spec.rb +193 -0
- data/spec/lib/frepl/function_spec.rb +43 -0
- data/spec/lib/frepl/multi_declaration_spec.rb +123 -0
- data/spec/lib/frepl/subroutine_spec.rb +46 -0
- data/spec/lib/frepl_spec.rb +256 -0
- data/spec/spec_helper.rb +106 -0
- metadata +186 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
3
|
+
module Frepl
|
4
|
+
class FortranFile
|
5
|
+
BEGIN_PROGRAM_STATEMENT = "program frepl_out\n"
|
6
|
+
END_PROGRAM_STATEMENT = "end program frepl_out\n"
|
7
|
+
FUNC_SUBROUTINE_HEADER = " contains\n"
|
8
|
+
IMPLICIT_STATEMENT = " implicit none\n"
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@all_statements = []
|
12
|
+
@declarations = []
|
13
|
+
@derived_types = []
|
14
|
+
@assignments = []
|
15
|
+
@execution = nil
|
16
|
+
@allocations = []
|
17
|
+
@subroutines = []
|
18
|
+
@functions = []
|
19
|
+
@wheres = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
File.open('frepl_out.f90', 'w+') do |f|
|
24
|
+
f << BEGIN_PROGRAM_STATEMENT
|
25
|
+
f << IMPLICIT_STATEMENT
|
26
|
+
|
27
|
+
@derived_types.each do |dt|
|
28
|
+
f.write(dt.output)
|
29
|
+
end
|
30
|
+
|
31
|
+
@declarations.each do |d|
|
32
|
+
f.write(d.output)
|
33
|
+
end
|
34
|
+
|
35
|
+
@allocations.each do |a|
|
36
|
+
f.write(a.output)
|
37
|
+
end
|
38
|
+
|
39
|
+
@assignments.each do |a|
|
40
|
+
f.write(a.output)
|
41
|
+
end
|
42
|
+
|
43
|
+
if @execution
|
44
|
+
f.write(@execution.output)
|
45
|
+
end
|
46
|
+
|
47
|
+
if @subroutines.any? || @functions.any?
|
48
|
+
f << FUNC_SUBROUTINE_HEADER
|
49
|
+
|
50
|
+
@subroutines.each do |sub|
|
51
|
+
f.write(sub.output)
|
52
|
+
end
|
53
|
+
|
54
|
+
@functions.each do |fn|
|
55
|
+
f.write(fn.output)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
f << END_PROGRAM_STATEMENT
|
60
|
+
end
|
61
|
+
o = `#{Frepl.compiler} frepl_out.f90 -o frepl_out && ./frepl_out`
|
62
|
+
Frepl.output(o)
|
63
|
+
end
|
64
|
+
|
65
|
+
def add(line_obj)
|
66
|
+
line_obj.accept(self)
|
67
|
+
@all_statements << line_obj
|
68
|
+
Frepl.log("added")
|
69
|
+
Frepl.log("declarations: #{@declarations}")
|
70
|
+
Frepl.log("assignments: #{@assignments}")
|
71
|
+
end
|
72
|
+
|
73
|
+
def undo_last!
|
74
|
+
last_statement = @all_statements.last
|
75
|
+
ivar = instance_variable_get("@#{last_statement.class.to_s.demodulize.underscore.pluralize}")
|
76
|
+
ivar.pop
|
77
|
+
end
|
78
|
+
|
79
|
+
def visit_declaration(declaration)
|
80
|
+
if i = @declarations.find_index { |d| d == declaration }
|
81
|
+
@declarations[i] = declaration
|
82
|
+
if j = @assignments.find_index { |a| a.variable_name == declaration.variable_name }
|
83
|
+
@assignments.slice!(j)
|
84
|
+
end
|
85
|
+
else
|
86
|
+
@declarations << declaration
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def visit_multi_declaration(multi_declaration)
|
91
|
+
multi_declaration.declarations.each do |d|
|
92
|
+
visit_declaration(d)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def visit_assignment(a)
|
97
|
+
@assignments << a
|
98
|
+
e = Execution.new(a.expressionize)
|
99
|
+
visit_execution(e)
|
100
|
+
Frepl.log("assignment name: #{a.variable_name}")
|
101
|
+
end
|
102
|
+
|
103
|
+
def visit_standalone_variable(sv)
|
104
|
+
e = Execution.new(sv.expressionize)
|
105
|
+
visit_execution(e)
|
106
|
+
end
|
107
|
+
|
108
|
+
def visit_allocation(a)
|
109
|
+
@allocations << a
|
110
|
+
end
|
111
|
+
|
112
|
+
def visit_execution(e)
|
113
|
+
@execution = e
|
114
|
+
run
|
115
|
+
end
|
116
|
+
|
117
|
+
def visit_repl_command(cmd)
|
118
|
+
cmd.run(self)
|
119
|
+
end
|
120
|
+
|
121
|
+
def visit_function(fn)
|
122
|
+
if i = @functions.find_index { |f| f == fn }
|
123
|
+
@functions[i] = fn
|
124
|
+
else
|
125
|
+
@functions << fn
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def visit_subroutine(sub)
|
130
|
+
if i = @subroutines.find_index { |s| s == sub }
|
131
|
+
@subroutines[i] = sub
|
132
|
+
else
|
133
|
+
@subroutines << sub
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def visit_ifstatement(i)
|
138
|
+
e = Execution.new(i.output)
|
139
|
+
visit_execution(e)
|
140
|
+
end
|
141
|
+
|
142
|
+
def visit_do_loop(d)
|
143
|
+
e = Execution.new(d.output)
|
144
|
+
visit_execution(e)
|
145
|
+
end
|
146
|
+
|
147
|
+
def visit_where(w)
|
148
|
+
@assignments << w
|
149
|
+
end
|
150
|
+
|
151
|
+
def visit_derived_type(dt)
|
152
|
+
if i = @derived_types.find_index { |v| v == dt }
|
153
|
+
@derived_types[i] = dt
|
154
|
+
else
|
155
|
+
@derived_types << dt
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Frepl
|
2
|
+
class Statement
|
3
|
+
def incomplete?
|
4
|
+
raise NotImplementedError
|
5
|
+
end
|
6
|
+
|
7
|
+
def output
|
8
|
+
raise NotImplementedError
|
9
|
+
end
|
10
|
+
|
11
|
+
def accept
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
# def accept(visitor)
|
16
|
+
# method = "visit_#{self.class.name.downcase}".to_sym
|
17
|
+
# visitor.public_send(method, visitor)
|
18
|
+
# end
|
19
|
+
end
|
20
|
+
|
21
|
+
class SinglelineStatement < Statement
|
22
|
+
attr_reader :line
|
23
|
+
|
24
|
+
def initialize(line)
|
25
|
+
@line = line
|
26
|
+
parse
|
27
|
+
end
|
28
|
+
|
29
|
+
def output
|
30
|
+
@line.to_s + "\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def incomplete?
|
34
|
+
false
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def parse
|
40
|
+
# override me at your leisure, to get parsing on initialization
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class MultilineStatement < Statement
|
45
|
+
attr_reader :lines
|
46
|
+
|
47
|
+
def initialize(lines = [])
|
48
|
+
@lines = lines
|
49
|
+
end
|
50
|
+
|
51
|
+
def output
|
52
|
+
@lines.join("\n") + "\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
def incomplete?
|
56
|
+
!complete?
|
57
|
+
end
|
58
|
+
|
59
|
+
def complete?
|
60
|
+
@lines.last.match(terminal_regex) != nil && !nested?
|
61
|
+
end
|
62
|
+
|
63
|
+
def terminal_regex
|
64
|
+
raise NotImplementedError
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def starting_regex
|
70
|
+
raise NotImplementedError
|
71
|
+
end
|
72
|
+
|
73
|
+
def nested?
|
74
|
+
start_count = lines.select { |line| line.match(/\A\s*#{starting_regex}\z/) != nil }.count
|
75
|
+
end_count = lines.select { |line| line.match(/\A\s*#{terminal_regex}\z/) != nil }.count
|
76
|
+
start_count > end_count
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
require 'frepl/statements/function'
|
82
|
+
require 'frepl/statements/subroutine'
|
83
|
+
require 'frepl/statements/declaration'
|
84
|
+
require 'frepl/statements/multi_declaration'
|
85
|
+
require 'frepl/statements/allocation'
|
86
|
+
require 'frepl/statements/assignment'
|
87
|
+
require 'frepl/statements/standalone_variable'
|
88
|
+
require 'frepl/statements/execution'
|
89
|
+
require 'frepl/statements/repl_command'
|
90
|
+
require 'frepl/statements/if_statement'
|
91
|
+
require 'frepl/statements/do_loop'
|
92
|
+
require 'frepl/statements/derived_type'
|
93
|
+
require 'frepl/statements/where'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Frepl
|
2
|
+
class Assignment < SinglelineStatement
|
3
|
+
def variable_name
|
4
|
+
@variable_name ||= @line.match(Frepl::Classifier::ASSIGNMENT_REGEX)[1]
|
5
|
+
end
|
6
|
+
|
7
|
+
def assigned_value
|
8
|
+
@assigned_value ||= @line.match(Frepl::Classifier::ASSIGNMENT_REGEX)[2]
|
9
|
+
end
|
10
|
+
|
11
|
+
def expressionize
|
12
|
+
"write(*,*) #{variable_name}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def accept(visitor)
|
16
|
+
visitor.visit_assignment(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Frepl
|
2
|
+
class Declaration < SinglelineStatement
|
3
|
+
attr_reader :variable_name, :assigned_value, :type, :len, :kind
|
4
|
+
|
5
|
+
def accept(visitor)
|
6
|
+
visitor.visit_declaration(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def ==(other)
|
10
|
+
if other.is_a?(Declaration)
|
11
|
+
self.variable_name == other.variable_name
|
12
|
+
else
|
13
|
+
super(other)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def target?
|
18
|
+
@target != nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def pointer?
|
22
|
+
@pointer != nil
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def parse
|
28
|
+
match_data = line.match(Frepl::Classifier::DECLARATION_REGEX)
|
29
|
+
variable_part = match_data[7]
|
30
|
+
variable_data = variable_part.match(/\s*(#{Frepl::Classifier::VARIABLE_NAME_REGEX})\s*+=*\s*(.*)?/)
|
31
|
+
@variable_name = variable_data[1]
|
32
|
+
kind_len = match_data[2]
|
33
|
+
if kind_len
|
34
|
+
value = kind_len.match(/=?+([^=\(\)(?:kind|len)]+)/)[1]
|
35
|
+
if kind_len.match(/kind/)
|
36
|
+
@kind = value
|
37
|
+
else
|
38
|
+
@len = value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
@assigned_value = variable_data[2].empty? ? nil : variable_data[2]
|
42
|
+
@type = match_data[1]
|
43
|
+
@target = match_data[5]
|
44
|
+
@pointer = match_data[6]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Frepl
|
2
|
+
class DerivedType < MultilineStatement
|
3
|
+
def terminal_regex
|
4
|
+
/end type\s*#{Frepl::Classifier::VARIABLE_NAME_REGEX}/i
|
5
|
+
end
|
6
|
+
|
7
|
+
def accept(visitor)
|
8
|
+
visitor.visit_derived_type(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= lines.first.match(Frepl::Classifier::DERIVED_TYPE_REGEX)[1]
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
if other.is_a?(DerivedType)
|
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::DERIVED_TYPE_REGEX
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Frepl
|
2
|
+
class Function < MultilineStatement
|
3
|
+
def terminal_regex
|
4
|
+
/end function/
|
5
|
+
end
|
6
|
+
|
7
|
+
def accept(visitor)
|
8
|
+
visitor.visit_function(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= lines.first.match(Frepl::Classifier::FUNCTION_REGEX)[2]
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
if other.is_a?(Function)
|
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::FUNCTION_REGEX
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Frepl
|
2
|
+
class IfStatement < MultilineStatement
|
3
|
+
def terminal_regex
|
4
|
+
/end\s?if/
|
5
|
+
end
|
6
|
+
|
7
|
+
def accept(visitor)
|
8
|
+
visitor.visit_ifstatement(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def starting_regex
|
14
|
+
Frepl::Classifier::IF_STATEMENT_REGEX
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Frepl
|
2
|
+
class MultiDeclaration < SinglelineStatement
|
3
|
+
attr_reader :variable_names
|
4
|
+
|
5
|
+
def accept(visitor)
|
6
|
+
visitor.visit_multi_declaration(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def declarations
|
10
|
+
@declarations ||= @variable_parts.map do |varpart|
|
11
|
+
str = "#{@type}"
|
12
|
+
str << "#{@kind_len}" if @kind_len
|
13
|
+
str << "#{@parameter}" if @parameter
|
14
|
+
str << "#{@dimension}" if @dimension
|
15
|
+
str << "#{@target}" if @target
|
16
|
+
str << "#{@pointer}" if @pointer
|
17
|
+
str << " :: #{varpart}"
|
18
|
+
Declaration.new(str)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# TODO regex needs better parameter/dimension parsing
|
25
|
+
def parse
|
26
|
+
match_data = line.match(Frepl::Classifier::DECLARATION_REGEX)
|
27
|
+
@type = match_data[1]
|
28
|
+
kind_len = match_data[2]
|
29
|
+
if @kind_len
|
30
|
+
if @kind_len.match(/len/)
|
31
|
+
@len = @kind_len.match(/=(\d+)/)[1]
|
32
|
+
else
|
33
|
+
@kind = @kind_len.match(/=(\d+)/)[1]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
@parameter = match_data[3]
|
37
|
+
@dimension = match_data[4]
|
38
|
+
@target = match_data[5]
|
39
|
+
@pointer = match_data[6]
|
40
|
+
@variable_names = match_data[7].gsub(/\s*=\s*/, '=').scan(Frepl::Classifier::VARIABLE_NAME_REGEX)
|
41
|
+
@variable_assignments = match_data[7].gsub(/\s*=\s*/, '=').
|
42
|
+
split(Frepl::Classifier::VARIABLE_NAME_REGEX)[1..-1].
|
43
|
+
map { |a| a.gsub(/,\s*$/, '') }
|
44
|
+
if @variable_assignments && @variable_assignments.any?
|
45
|
+
if @variable_assignments.size < @variable_names.size
|
46
|
+
# e.g., integer :: a = 1, b, c
|
47
|
+
mask = match_data[7].gsub(/\s*=\s*/, '=').
|
48
|
+
scan(/#{Frepl::Classifier::VARIABLE_NAME_REGEX}[=,$\z]?/).
|
49
|
+
map { |x| x.match(/=/) ? '=' : '' }
|
50
|
+
arr = []
|
51
|
+
# pad var assignments array, determine correct locations
|
52
|
+
mask.each do |m|
|
53
|
+
if m == '='
|
54
|
+
arr.push(@variable_assignments.shift)
|
55
|
+
else
|
56
|
+
arr.push('')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
@variable_assignments = arr
|
60
|
+
end
|
61
|
+
# should be something like [['a', 'b'], ['=1', '']]
|
62
|
+
@variable_parts = [@variable_names, @variable_assignments].transpose.map do |variable_parts|
|
63
|
+
variable_parts.join('')
|
64
|
+
end
|
65
|
+
else
|
66
|
+
@variable_parts = @variable_names
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|