frepl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,9 @@
1
+ require 'frepl/statements/declaration'
2
+
3
+ module Frepl
4
+ class Allocation < Declaration
5
+ def accept(visitor)
6
+ visitor.visit_allocation(self)
7
+ end
8
+ end
9
+ end
@@ -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,17 @@
1
+ module Frepl
2
+ class DoLoop < MultilineStatement
3
+ def terminal_regex
4
+ /end do/
5
+ end
6
+
7
+ def accept(visitor)
8
+ visitor.visit_do_loop(self)
9
+ end
10
+
11
+ private
12
+
13
+ def starting_regex
14
+ Frepl::Classifier::DO_LOOP_REGEX
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ module Frepl
2
+ class Execution < SinglelineStatement
3
+ def accept(visitor)
4
+ visitor.visit_execution(self)
5
+ end
6
+ end
7
+ 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