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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9b623779b0cd2543b859da5e7b63195c7e54f6e8
|
4
|
+
data.tar.gz: fde5b1a7e38e9ef4b8f1a2a1897dd875fef2de71
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c532180ba00cd864128c61d421a9fa9328eb86490c74a3554377583b11535e1542bf0e1f73bb1956b82b8d8ce4b7c820fb2fccba2f5b676957cbc876fae4d965
|
7
|
+
data.tar.gz: 8b0af0047ebd2691dae341d4dd57944800062e0a44e096f4f6d0a33e5db5190202f5b03624da4749f838244c6643b9f055f66b1c57cde9e321982d1e54108fcf
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Luke Rodgers
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# Frepl
|
2
|
+
|
3
|
+
Frepl (Fortran REPL) is an experimental ruby-based REPL for Fortran,
|
4
|
+
that I wrote because I was trying to learn Fortran, and I find the feedback
|
5
|
+
loop you get with a REPL makes learning a language much easier and more
|
6
|
+
enjoyable.
|
7
|
+
|
8
|
+
You don't need to know ruby to use Frepl, but you do need to have ruby (at least version 2)
|
9
|
+
installed.
|
10
|
+
|
11
|
+
There are a lot of deficiencies with this code, namely:
|
12
|
+
|
13
|
+
* Only knows how to classify/parse a limited set of Fortran.
|
14
|
+
* IO is severely hampered; basically you can't do the I part.
|
15
|
+
I have some ideas about how to sort of accomplish this but
|
16
|
+
they are half-baked and convoluted. Also I'm not sure this is
|
17
|
+
even really that important.
|
18
|
+
* Parsing is pretty dumb. It uses complicated and somewhat opaque regexes in places
|
19
|
+
where a real lexer/parser approach might be more appropriate, though might also be
|
20
|
+
overkill.
|
21
|
+
|
22
|
+
## Project plans
|
23
|
+
|
24
|
+
See issue tracker.
|
25
|
+
|
26
|
+
## Known issues
|
27
|
+
|
28
|
+
* Frepl is only able to correctly classify a subset of legal Fortran. At the same time,
|
29
|
+
Frepl will also happily accept some illegal Fortran, and wait for the compiler to tell you
|
30
|
+
about the problem. The UX here is not great.
|
31
|
+
* When specifying parameter and dimension in a declaration, parameter must currently come first,
|
32
|
+
e.g. `integer, parameter, dimension(:) :: a`.
|
33
|
+
* only `do... end do` loops are supported
|
34
|
+
* no support for labels; hence, no GOTO and its ilk
|
35
|
+
|
36
|
+
## Installation
|
37
|
+
|
38
|
+
Add this line to your application's Gemfile:
|
39
|
+
|
40
|
+
gem 'frepl'
|
41
|
+
|
42
|
+
And then execute:
|
43
|
+
|
44
|
+
$ bundle
|
45
|
+
|
46
|
+
Or install it yourself as:
|
47
|
+
|
48
|
+
$ gem install frepl
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
You should be able to run `frepl` from the command line after having installed the gem.
|
53
|
+
|
54
|
+
Alternatively, if you have the source code, you can run `rake console` from the gem folder.
|
55
|
+
|
56
|
+
You will get a prompt, and you can just start typing Fortran, type `q` to quit.
|
57
|
+
|
58
|
+
```
|
59
|
+
> integer :: a = 1
|
60
|
+
> integer, dimension(:), allocatable :: b
|
61
|
+
> allocate(b(0:4))
|
62
|
+
> b = [1,2,3,4]
|
63
|
+
> write(*,*) a * b
|
64
|
+
1 2 3 4
|
65
|
+
> q
|
66
|
+
```
|
67
|
+
|
68
|
+
### Redefine a function, change the type of a variable, etc.
|
69
|
+
|
70
|
+
```
|
71
|
+
> integer :: a = 1
|
72
|
+
> a = 3
|
73
|
+
3
|
74
|
+
> real a
|
75
|
+
> a = 5
|
76
|
+
5.00000000
|
77
|
+
> integer function sum(a, b)
|
78
|
+
> integer, intent(in) :: a, b
|
79
|
+
> sum = a + b
|
80
|
+
> end function
|
81
|
+
> write(*,*) sum(1,2)
|
82
|
+
3
|
83
|
+
> real function sum(a, b, c)
|
84
|
+
> real, intent(in) :: a, b, c
|
85
|
+
> sum = a + b + c
|
86
|
+
> end function
|
87
|
+
> write(*,*) sum(1.0,2.9,3.4)
|
88
|
+
7.30000019
|
89
|
+
>
|
90
|
+
```
|
91
|
+
|
92
|
+
### Undo the last statement
|
93
|
+
|
94
|
+
Type `f:z` (z as in cmd+z for undo). Handy when you made an error, e.g.
|
95
|
+
|
96
|
+
```
|
97
|
+
> integer :: a =3
|
98
|
+
> real :: b
|
99
|
+
> b = 'fo'
|
100
|
+
frepl_out.f90:5.4:
|
101
|
+
|
102
|
+
b = 'fo'
|
103
|
+
1
|
104
|
+
Error: Can't convert CHARACTER(1) to REAL(4) at (1)
|
105
|
+
|
106
|
+
> f:z
|
107
|
+
> b = 3.4
|
108
|
+
3.40000010
|
109
|
+
>
|
110
|
+
```
|
111
|
+
|
112
|
+
You can see some repl commands by typing `f:help`. Not much going on there, currently.
|
113
|
+
|
114
|
+
## Contributing
|
115
|
+
|
116
|
+
1. Fork it ( http://github.com/<my-github-username>/frepl/fork )
|
117
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
118
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
119
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
120
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'frepl'
|
3
|
+
|
4
|
+
task :console do
|
5
|
+
require 'pry'
|
6
|
+
require 'frepl'
|
7
|
+
|
8
|
+
def reload!
|
9
|
+
files = $LOADED_FEATURES.select { |feat| feat =~ /\/frepl\// }
|
10
|
+
files.each { |file| load file }
|
11
|
+
end
|
12
|
+
|
13
|
+
ARGV.clear
|
14
|
+
Pry.start
|
15
|
+
end
|
data/bin/frepl
ADDED
data/frepl.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'frepl/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "frepl"
|
8
|
+
spec.version = Frepl::VERSION
|
9
|
+
spec.authors = ["Luke Rodgers"]
|
10
|
+
spec.email = ["lukeasrodgers@gmail.com"]
|
11
|
+
spec.summary = %q{A hacky, experimental Fortran REPL in ruby}
|
12
|
+
spec.description = %q{(Badly) supports a small subset of Fortran to be run in a REPL-like environment, with a bunch of caveats.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'activesupport', '> 4'
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "pry"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.3"
|
27
|
+
spec.add_development_dependency "rspec-mocks", "~> 3.3"
|
28
|
+
spec.add_development_dependency "byebug"
|
29
|
+
end
|
data/lib/frepl.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'readline'
|
2
|
+
require 'frepl/version'
|
3
|
+
require 'frepl/classifier'
|
4
|
+
require 'frepl/statement'
|
5
|
+
require 'frepl/fortran_file'
|
6
|
+
|
7
|
+
module Frepl
|
8
|
+
extend self
|
9
|
+
|
10
|
+
attr_accessor :compiler, :debug
|
11
|
+
|
12
|
+
def log(message)
|
13
|
+
puts message if @debug
|
14
|
+
end
|
15
|
+
|
16
|
+
def output(o)
|
17
|
+
puts o
|
18
|
+
end
|
19
|
+
|
20
|
+
class Main
|
21
|
+
class << self
|
22
|
+
def run
|
23
|
+
new.run
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
Frepl.compiler = 'gfortran'
|
29
|
+
Frepl.debug = false
|
30
|
+
reset!
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
loop do
|
35
|
+
begin
|
36
|
+
while buf = Readline.readline(prompt, true)
|
37
|
+
@lines << buf
|
38
|
+
process_line(buf)
|
39
|
+
end
|
40
|
+
rescue Interrupt
|
41
|
+
@classifier.interrupt
|
42
|
+
puts "^C\n"
|
43
|
+
rescue SystemExit, SignalException
|
44
|
+
raise
|
45
|
+
rescue Exception => e
|
46
|
+
puts "Exception!: #{e}"
|
47
|
+
puts e.backtrace
|
48
|
+
raise
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def run_file(file)
|
54
|
+
file.each do |line|
|
55
|
+
@lines << line
|
56
|
+
process_line(line)
|
57
|
+
end
|
58
|
+
reset!
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def prompt
|
64
|
+
'> ' + (' ' * @classifier.indentation_level)
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_line(line)
|
68
|
+
exit(0) if line.chomp == 'q'
|
69
|
+
Frepl.log("classifying: #{line}")
|
70
|
+
line_obj = @classifier.classify(line)
|
71
|
+
@file.add(line_obj) unless line_obj.nil? || line_obj.incomplete?
|
72
|
+
end
|
73
|
+
|
74
|
+
def reset!
|
75
|
+
@classifier = Classifier.new
|
76
|
+
@file = FortranFile.new
|
77
|
+
@lines = []
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module Frepl
|
2
|
+
class Classifier
|
3
|
+
VARIABLE_NAME_REGEX = /[a-zA-Z][a-zA-Z0-9_]{,30}/
|
4
|
+
# TODO this regex seems incorrect, e.g. assignable value can't start with most punctuation
|
5
|
+
ASSIGNABLE_VALUE_REGEX = /[^\s]+/
|
6
|
+
DERIVED_TYPE_IDENTIFIER_REGEX = /\(#{VARIABLE_NAME_REGEX}\)/
|
7
|
+
DERIVED_TYPE_REGEX = /type\s+(#{VARIABLE_NAME_REGEX})/i
|
8
|
+
BUILTIN_TYPE_REGEX = /real|integer|character|logical/i
|
9
|
+
DECLARABLE_TYPE_REGEX = /#{BUILTIN_TYPE_REGEX}|type\s\(#{VARIABLE_NAME_REGEX}\)\s/i
|
10
|
+
# TODO: parameter/dimension order shouldn't matter here
|
11
|
+
DECLARATION_REGEX = /\As*(#{DECLARABLE_TYPE_REGEX})\s*(\((?:kind=|len=)*[^\(\)]+\)){,1}+(\s*,?\s*parameter\s*,?\s*)?(\s*,?\s*dimension\([^\)]+\))?(\s*,?\s*target)?(\s*,?\s*pointer)?\s*(?:::)?\s*([^(?:::)]*)/
|
12
|
+
ASSIGNABLE_REGEX = /#{VARIABLE_NAME_REGEX}|#{VARIABLE_NAME_REGEX}%#{VARIABLE_NAME_REGEX}/
|
13
|
+
ASSIGNMENT_REGEX = /\As*(#{ASSIGNABLE_REGEX})\s*=\s*(#{ASSIGNABLE_VALUE_REGEX})/
|
14
|
+
OLDSKOOL_ARRAY_VALUE_REGEX = /\/[^\]]+\//
|
15
|
+
F2003_ARRAY_VALUE_REGEX = /\[[^\]]+\]/
|
16
|
+
ARRAY_VALUE_REGEX = /#{OLDSKOOL_ARRAY_VALUE_REGEX}|#{F2003_ARRAY_VALUE_REGEX}/
|
17
|
+
FUNCTION_REGEX = /(#{BUILTIN_TYPE_REGEX})\s+function\s+(#{VARIABLE_NAME_REGEX})/i
|
18
|
+
SUBROUTINE_REGEX = /subroutine\s+(#{VARIABLE_NAME_REGEX})/i
|
19
|
+
IF_STATEMENT_REGEX = /if\s+\(.+\)\sthen/i
|
20
|
+
DO_LOOP_REGEX = /do\s+[^,]+,.+/i
|
21
|
+
WHERE_REGEX = /where\s+\([^\)]+\)\s*/i
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@all_lines = []
|
25
|
+
@current_lines = []
|
26
|
+
@current_multiline_obj = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def classify(line)
|
30
|
+
if @current_multiline_obj && !@current_multiline_obj.incomplete?
|
31
|
+
@current_multiline_obj = nil
|
32
|
+
@current_lines = []
|
33
|
+
end
|
34
|
+
|
35
|
+
@all_lines << line
|
36
|
+
@current_lines << line
|
37
|
+
|
38
|
+
if multiline?(line)
|
39
|
+
Frepl.log("MULTILINE")
|
40
|
+
classify_multiline(line)
|
41
|
+
return @current_multiline_obj
|
42
|
+
else
|
43
|
+
return classify_single_line(line)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def interrupt
|
48
|
+
@current_multiline_obj = nil
|
49
|
+
@current_lines = []
|
50
|
+
end
|
51
|
+
|
52
|
+
def executable?
|
53
|
+
!get_more_lines?
|
54
|
+
end
|
55
|
+
|
56
|
+
def lines_to_execute
|
57
|
+
@current_lines
|
58
|
+
end
|
59
|
+
|
60
|
+
def multiline?(line)
|
61
|
+
line.match(/\Asubroutine|function|(?:#{IF_STATEMENT_REGEX})|(?:#{DO_LOOP_REGEX})|(?:#{DERIVED_TYPE_REGEX})|(?:#{WHERE_REGEX})\z/i) || @current_multiline_obj != nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def repl_command?
|
65
|
+
current_line.start_with?('f:')
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO this is stupid, may need real parser here
|
69
|
+
def multi_declaration?
|
70
|
+
m = current_line.match(DECLARATION_REGEX)
|
71
|
+
return false unless m
|
72
|
+
if m[7].gsub(ARRAY_VALUE_REGEX, '').count(',') > 0
|
73
|
+
true
|
74
|
+
else
|
75
|
+
false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# TODO this is stupid, may need real parser here
|
80
|
+
def declaration?
|
81
|
+
m = current_line.match(DECLARATION_REGEX)
|
82
|
+
return false unless m
|
83
|
+
if m[7].gsub(ARRAY_VALUE_REGEX, '').count(',') == 0
|
84
|
+
true
|
85
|
+
else
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def allocation?
|
91
|
+
current_line.match(/allocate\(/) != nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def run?
|
95
|
+
current_line.match(/\s*write|print|read|call/i) != nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def assignment?
|
99
|
+
current_line.match(ASSIGNMENT_REGEX)
|
100
|
+
end
|
101
|
+
|
102
|
+
def standalone_variable?
|
103
|
+
current_line.match(/\A#{VARIABLE_NAME_REGEX}\z/)
|
104
|
+
end
|
105
|
+
|
106
|
+
def indentation_level
|
107
|
+
if @current_multiline_obj && @current_multiline_obj.incomplete?
|
108
|
+
2
|
109
|
+
else
|
110
|
+
0
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def classify_multiline(line)
|
117
|
+
if @current_lines.size == 1
|
118
|
+
raise 'Already have multiline obj' unless @current_multiline_obj.nil?
|
119
|
+
if line.match(/function/)
|
120
|
+
@current_multiline_obj = Function.new
|
121
|
+
elsif line.match(/subroutine/)
|
122
|
+
@current_multiline_obj = Subroutine.new
|
123
|
+
elsif line.match(IF_STATEMENT_REGEX)
|
124
|
+
@current_multiline_obj = IfStatement.new
|
125
|
+
elsif line.match(DO_LOOP_REGEX)
|
126
|
+
@current_multiline_obj = DoLoop.new
|
127
|
+
elsif line.match(/\A#{DERIVED_TYPE_REGEX}\z/)
|
128
|
+
@current_multiline_obj = DerivedType.new
|
129
|
+
elsif line.match(/\A#{WHERE_REGEX}\z/)
|
130
|
+
@current_multiline_obj = Where.new
|
131
|
+
end
|
132
|
+
end
|
133
|
+
@current_multiline_obj.lines << line
|
134
|
+
end
|
135
|
+
|
136
|
+
def classify_single_line(line)
|
137
|
+
obj = if repl_command?
|
138
|
+
ReplCommand.new(line)
|
139
|
+
elsif multi_declaration?
|
140
|
+
MultiDeclaration.new(line)
|
141
|
+
elsif declaration?
|
142
|
+
Declaration.new(line)
|
143
|
+
elsif run?
|
144
|
+
Execution.new(line)
|
145
|
+
elsif assignment?
|
146
|
+
Assignment.new(line)
|
147
|
+
elsif allocation?
|
148
|
+
Allocation.new(line)
|
149
|
+
elsif standalone_variable?
|
150
|
+
StandaloneVariable.new(line)
|
151
|
+
elsif ignorable?
|
152
|
+
nil
|
153
|
+
else
|
154
|
+
puts "I don't think `#{line}` is valid Fortran. You made a mistake, or, more likely, I'm dumb."
|
155
|
+
@all_lines.pop
|
156
|
+
obj = nil
|
157
|
+
end
|
158
|
+
@current_lines = []
|
159
|
+
obj
|
160
|
+
end
|
161
|
+
|
162
|
+
def current_line
|
163
|
+
@current_lines.last
|
164
|
+
end
|
165
|
+
|
166
|
+
def get_more_lines?
|
167
|
+
@current_lines.size > 0 || @current_lines.last.match(/subroutine|function/)
|
168
|
+
end
|
169
|
+
|
170
|
+
def done_multiline?
|
171
|
+
@current_lines.size > 0 && @current_lines.last.match(/end subroutine|end function/)
|
172
|
+
end
|
173
|
+
|
174
|
+
def ignorable?
|
175
|
+
program_statements? || blank? || comment?
|
176
|
+
end
|
177
|
+
|
178
|
+
def program_statements?
|
179
|
+
current_line.match(/\Aprogram .+|implicit .+|\Aend program/) != nil
|
180
|
+
end
|
181
|
+
|
182
|
+
def blank?
|
183
|
+
current_line.match(/\A\s*\z/)
|
184
|
+
end
|
185
|
+
|
186
|
+
def comment?
|
187
|
+
current_line.match(/\A!/)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|