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