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,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