ritsu 0.7.0 → 0.7.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.
- data/README.md +95 -95
- data/bin/define_cpp_string +54 -54
- data/bin/ritsu +31 -31
- data/lib/ritsu.rb +16 -16
- data/lib/ritsu/block.rb +258 -258
- data/lib/ritsu/ext/cuda.rb +5 -5
- data/lib/ritsu/ext/cuda/external_library.rb +15 -15
- data/lib/ritsu/ext/cuda/project.rb +31 -31
- data/lib/ritsu/ext/cuda/src_files/cu_file.rb +46 -46
- data/lib/ritsu/ext/cuda/src_files/target_cmake_lists.rb +110 -110
- data/lib/ritsu/ext/cuda/target.rb +16 -16
- data/lib/ritsu/ext/cuda/targets/library.rb +16 -16
- data/lib/ritsu/ext/fake_install.rb +2 -2
- data/lib/ritsu/ext/fake_install/project.rb +16 -16
- data/lib/ritsu/ext/fake_install/src_files/project_cmake_lists.rb +45 -45
- data/lib/ritsu/ext/glsl.rb +1 -1
- data/lib/ritsu/ext/glsl/src_files/frag_file.rb +70 -70
- data/lib/ritsu/ext/glsl/src_files/vert_file.rb +70 -70
- data/lib/ritsu/ext/qt.rb +4 -4
- data/lib/ritsu/ext/qt/project.rb +47 -47
- data/lib/ritsu/ext/qt/src_files/header_file.rb +60 -60
- data/lib/ritsu/ext/qt/src_files/target_cmake_lists.rb +106 -106
- data/lib/ritsu/ext/qt/src_files/ui_file.rb +46 -46
- data/lib/ritsu/ext/test_case.rb +19 -19
- data/lib/ritsu/external_library.rb +46 -46
- data/lib/ritsu/project.rb +93 -93
- data/lib/ritsu/project_generator.rb +33 -33
- data/lib/ritsu/project_generators/default_generator.rb +72 -72
- data/lib/ritsu/project_generators/default_generator_files/Thorfile.erb +8 -8
- data/lib/ritsu/project_generators/default_generator_files/meta/project.rb.erb +10 -10
- data/lib/ritsu/src_file.rb +79 -79
- data/lib/ritsu/src_files.rb +12 -12
- data/lib/ritsu/src_files/cpp_file.rb +43 -43
- data/lib/ritsu/src_files/executable_cmake_lists.rb +39 -39
- data/lib/ritsu/src_files/header_file.rb +60 -60
- data/lib/ritsu/src_files/project_cmake_lists.rb +133 -133
- data/lib/ritsu/src_files/project_config_header_file.rb +14 -14
- data/lib/ritsu/src_files/project_config_header_template_file.rb +44 -44
- data/lib/ritsu/src_files/shared_library_cmake_lists.rb +39 -39
- data/lib/ritsu/src_files/static_library_cmake_lists.rb +39 -39
- data/lib/ritsu/src_files/target_cmake_lists.rb +189 -189
- data/lib/ritsu/src_files/templated_src_file.rb +47 -47
- data/lib/ritsu/src_files/unit.rb +32 -32
- data/lib/ritsu/target.rb +154 -154
- data/lib/ritsu/targets.rb +3 -3
- data/lib/ritsu/targets/executable.rb +44 -44
- data/lib/ritsu/targets/library.rb +29 -29
- data/lib/ritsu/targets/shared_library.rb +38 -38
- data/lib/ritsu/targets/static_library.rb +32 -32
- data/lib/ritsu/template.rb +68 -68
- data/lib/ritsu/template_policies.rb +132 -132
- data/lib/ritsu/test_helpers.rb +123 -123
- data/lib/ritsu/thors/default_thor.rb +1 -1
- data/lib/ritsu/utility.rb +7 -7
- data/lib/ritsu/utility/accessors.rb +29 -29
- data/lib/ritsu/utility/check_upon_add_set.rb +34 -34
- data/lib/ritsu/utility/file_robot.rb +128 -128
- data/lib/ritsu/utility/files.rb +12 -12
- data/lib/ritsu/utility/instance_dependencies.rb +112 -112
- data/lib/ritsu/utility/instance_set.rb +28 -28
- data/lib/ritsu/utility/platform.rb +20 -20
- data/lib/ritsu/utility/simple_io.rb +64 -64
- data/lib/ritsu/utility/single_instance.rb +33 -33
- data/lib/ritsu/utility/strings.rb +40 -40
- data/test/ritsu/block_test.rb +196 -196
- data/test/ritsu/ext/cuda/src_files/cuda_static_library_cmake_lists_test.rb +63 -63
- data/test/ritsu/external_library_test.rb +41 -41
- data/test/ritsu/project_generators/default_generator_test.rb +34 -34
- data/test/ritsu/project_test.rb +127 -127
- data/test/ritsu/src_file_test.rb +69 -69
- data/test/ritsu/src_files/cpp_file_test.rb +42 -42
- data/test/ritsu/src_files/executable_cmake_lists_test.rb +92 -92
- data/test/ritsu/src_files/header_file_test.rb +57 -57
- data/test/ritsu/src_files/project_cmake_lists_test.rb +159 -159
- data/test/ritsu/src_files/shared_library_cmake_lists_test.rb +54 -54
- data/test/ritsu/src_files/static_library_cmake_lists_test.rb +54 -54
- data/test/ritsu/src_files/target_cmake_lists_test.rb +15 -15
- data/test/ritsu/target_test.rb +105 -105
- data/test/ritsu/targets/executable_test.rb +10 -10
- data/test/ritsu/targets/shared_library_test.rb +10 -10
- data/test/ritsu/targets/static_library_test.rb +10 -10
- data/test/ritsu/utility/accessors_test.rb +14 -14
- data/test/ritsu/utility/check_upon_add_set_test.rb +31 -31
- data/test/ritsu/utility/file_robot_test.rb +175 -175
- data/test/ritsu/utility/strings_test.rb +28 -28
- data/test/test_helpers.rb +3 -3
- metadata +72 -124
- data/Thorfile +0 -104
- data/VERSION +0 -1
data/README.md
CHANGED
@@ -1,96 +1,96 @@
|
|
1
|
-
# Ritsu
|
2
|
-
|
3
|
-
Ritsu helps you with C/C++ software development. It provides you with a
|
4
|
-
domain specific language to specify a C/C++ project, and then automates
|
5
|
-
build project generation with the help of CMake.
|
6
|
-
|
7
|
-
## Using
|
8
|
-
|
9
|
-
### Generate a Project
|
10
|
-
|
11
|
-
Run
|
12
|
-
|
13
|
-
ritsu my_project
|
14
|
-
|
15
|
-
in command prompt to generate a project.
|
16
|
-
This will generate directory with the following contents:
|
17
|
-
|
18
|
-
my_project
|
19
|
-
build
|
20
|
-
meta
|
21
|
-
project.rb
|
22
|
-
src
|
23
|
-
cmake_modules
|
24
|
-
--empty--
|
25
|
-
Thorfile
|
26
|
-
|
27
|
-
### Specifying the Project
|
28
|
-
|
29
|
-
The file <tt>meta/project.rb</tt> specifies the project, and its
|
30
|
-
content is initially as follows.
|
31
|
-
|
32
|
-
require 'ritsu'
|
33
|
-
|
34
|
-
Ritsu::Project.create('my_project') do |p|
|
35
|
-
|
36
|
-
##################
|
37
|
-
# YOUR CODE HERE #
|
38
|
-
##################
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
You now can populate the <tt>src</tt> directory with your C/C++ source
|
43
|
-
and header files. For examples:
|
44
|
-
|
45
|
-
my_project
|
46
|
-
build
|
47
|
-
meta
|
48
|
-
project.rb
|
49
|
-
src
|
50
|
-
cmake_modules
|
51
|
-
my_program
|
52
|
-
lib.cpp
|
53
|
-
lib.h
|
54
|
-
main.cpp
|
55
|
-
Thorfile
|
56
|
-
|
57
|
-
|
58
|
-
Then, you can specify targets to build as follows:
|
59
|
-
|
60
|
-
require 'ritsu'
|
61
|
-
|
62
|
-
Ritsu::Project.create('my_project') do |p|
|
63
|
-
p.add_executable('my_program') do |e|
|
64
|
-
e.add_cpp_file 'main.cpp'
|
65
|
-
e.add_header_file 'lib.h'
|
66
|
-
e.add_cpp_file 'lib.cpp'
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
As you can see, for each target (an executable or a library),
|
71
|
-
you have to create a directory under <tt>src</tt>, which bears the
|
72
|
-
same name as the project. The file names used in the <tt>add_cpp_file</tt>
|
73
|
-
and <tt>add_header_file</tt> commands are relative to the directory of the target.
|
74
|
-
You can bypass this behavior though.
|
75
|
-
|
76
|
-
### Building
|
77
|
-
|
78
|
-
To build the project, you first need to update the source files and
|
79
|
-
the <tt>CMakeLists.txt</tt> files for the project and for its targets.
|
80
|
-
This is done by running a Thor command in the project directory.
|
81
|
-
|
82
|
-
thor my_project:update_src
|
83
|
-
|
84
|
-
The source files and <tt>CMakeList</tt> files are generated if they are
|
85
|
-
not present. From this point, you can either run cmake to
|
86
|
-
generate platform-specific build scripts, or run
|
87
|
-
|
88
|
-
thor my_project:cmake
|
89
|
-
|
90
|
-
which will create an out-of-source build scripts in the <tt>build</tt> directory.
|
91
|
-
The action
|
92
|
-
|
93
|
-
thor my_project:update
|
94
|
-
|
95
|
-
combines the first two steps in one shot. Now, use your platform-specific
|
1
|
+
# Ritsu
|
2
|
+
|
3
|
+
Ritsu helps you with C/C++ software development. It provides you with a
|
4
|
+
domain specific language to specify a C/C++ project, and then automates
|
5
|
+
build project generation with the help of CMake.
|
6
|
+
|
7
|
+
## Using
|
8
|
+
|
9
|
+
### Generate a Project
|
10
|
+
|
11
|
+
Run
|
12
|
+
|
13
|
+
ritsu my_project
|
14
|
+
|
15
|
+
in command prompt to generate a project.
|
16
|
+
This will generate directory with the following contents:
|
17
|
+
|
18
|
+
my_project
|
19
|
+
build
|
20
|
+
meta
|
21
|
+
project.rb
|
22
|
+
src
|
23
|
+
cmake_modules
|
24
|
+
--empty--
|
25
|
+
Thorfile
|
26
|
+
|
27
|
+
### Specifying the Project
|
28
|
+
|
29
|
+
The file <tt>meta/project.rb</tt> specifies the project, and its
|
30
|
+
content is initially as follows.
|
31
|
+
|
32
|
+
require 'ritsu'
|
33
|
+
|
34
|
+
Ritsu::Project.create('my_project') do |p|
|
35
|
+
|
36
|
+
##################
|
37
|
+
# YOUR CODE HERE #
|
38
|
+
##################
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
You now can populate the <tt>src</tt> directory with your C/C++ source
|
43
|
+
and header files. For examples:
|
44
|
+
|
45
|
+
my_project
|
46
|
+
build
|
47
|
+
meta
|
48
|
+
project.rb
|
49
|
+
src
|
50
|
+
cmake_modules
|
51
|
+
my_program
|
52
|
+
lib.cpp
|
53
|
+
lib.h
|
54
|
+
main.cpp
|
55
|
+
Thorfile
|
56
|
+
|
57
|
+
|
58
|
+
Then, you can specify targets to build as follows:
|
59
|
+
|
60
|
+
require 'ritsu'
|
61
|
+
|
62
|
+
Ritsu::Project.create('my_project') do |p|
|
63
|
+
p.add_executable('my_program') do |e|
|
64
|
+
e.add_cpp_file 'main.cpp'
|
65
|
+
e.add_header_file 'lib.h'
|
66
|
+
e.add_cpp_file 'lib.cpp'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
As you can see, for each target (an executable or a library),
|
71
|
+
you have to create a directory under <tt>src</tt>, which bears the
|
72
|
+
same name as the project. The file names used in the <tt>add_cpp_file</tt>
|
73
|
+
and <tt>add_header_file</tt> commands are relative to the directory of the target.
|
74
|
+
You can bypass this behavior though.
|
75
|
+
|
76
|
+
### Building
|
77
|
+
|
78
|
+
To build the project, you first need to update the source files and
|
79
|
+
the <tt>CMakeLists.txt</tt> files for the project and for its targets.
|
80
|
+
This is done by running a Thor command in the project directory.
|
81
|
+
|
82
|
+
thor my_project:update_src
|
83
|
+
|
84
|
+
The source files and <tt>CMakeList</tt> files are generated if they are
|
85
|
+
not present. From this point, you can either run cmake to
|
86
|
+
generate platform-specific build scripts, or run
|
87
|
+
|
88
|
+
thor my_project:cmake
|
89
|
+
|
90
|
+
which will create an out-of-source build scripts in the <tt>build</tt> directory.
|
91
|
+
The action
|
92
|
+
|
93
|
+
thor my_project:update
|
94
|
+
|
95
|
+
combines the first two steps in one shot. Now, use your platform-specific
|
96
96
|
tool to build the project.
|
data/bin/define_cpp_string
CHANGED
@@ -1,54 +1,54 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# Convert a text file's content into a C++ const string.
|
3
|
-
|
4
|
-
# Turn the given string to
|
5
|
-
# a C string literal that
|
6
|
-
# parses to the original string.
|
7
|
-
def string_literal(s)
|
8
|
-
result = "\"\\\n"
|
9
|
-
s.each_byte do |b|
|
10
|
-
c = b.chr
|
11
|
-
if c == "\n"
|
12
|
-
result += "\\n\\\n"
|
13
|
-
elsif c == "\r"
|
14
|
-
next
|
15
|
-
elsif c == "\\"
|
16
|
-
result += "\\\\"
|
17
|
-
elsif c == "\""
|
18
|
-
result += "\\\""
|
19
|
-
else
|
20
|
-
result += c
|
21
|
-
end
|
22
|
-
end
|
23
|
-
result += "\""
|
24
|
-
result
|
25
|
-
end
|
26
|
-
|
27
|
-
# Declare a constant string
|
28
|
-
# variable whose value
|
29
|
-
# is the given string.
|
30
|
-
def const_string(name, s)
|
31
|
-
result = "const char * #{name} = " + string_literal(s) + ";"
|
32
|
-
end
|
33
|
-
|
34
|
-
require 'optparse'
|
35
|
-
|
36
|
-
parser = OptionParser.new do |parser|
|
37
|
-
parser.banner = "Usage: define_cpp_string variable_name"
|
38
|
-
|
39
|
-
parser.on_tail("-h", "--help", "Show this message") do
|
40
|
-
puts parser
|
41
|
-
exit
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
parser.parse(ARGV)
|
46
|
-
var_name = ARGV.shift
|
47
|
-
if not var_name
|
48
|
-
puts parser
|
49
|
-
exit
|
50
|
-
end
|
51
|
-
|
52
|
-
s = STDIN.read
|
53
|
-
|
54
|
-
puts const_string(var_name, s)
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Convert a text file's content into a C++ const string.
|
3
|
+
|
4
|
+
# Turn the given string to
|
5
|
+
# a C string literal that
|
6
|
+
# parses to the original string.
|
7
|
+
def string_literal(s)
|
8
|
+
result = "\"\\\n"
|
9
|
+
s.each_byte do |b|
|
10
|
+
c = b.chr
|
11
|
+
if c == "\n"
|
12
|
+
result += "\\n\\\n"
|
13
|
+
elsif c == "\r"
|
14
|
+
next
|
15
|
+
elsif c == "\\"
|
16
|
+
result += "\\\\"
|
17
|
+
elsif c == "\""
|
18
|
+
result += "\\\""
|
19
|
+
else
|
20
|
+
result += c
|
21
|
+
end
|
22
|
+
end
|
23
|
+
result += "\""
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
# Declare a constant string
|
28
|
+
# variable whose value
|
29
|
+
# is the given string.
|
30
|
+
def const_string(name, s)
|
31
|
+
result = "const char * #{name} = " + string_literal(s) + ";"
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'optparse'
|
35
|
+
|
36
|
+
parser = OptionParser.new do |parser|
|
37
|
+
parser.banner = "Usage: define_cpp_string variable_name"
|
38
|
+
|
39
|
+
parser.on_tail("-h", "--help", "Show this message") do
|
40
|
+
puts parser
|
41
|
+
exit
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
parser.parse(ARGV)
|
46
|
+
var_name = ARGV.shift
|
47
|
+
if not var_name
|
48
|
+
puts parser
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
|
52
|
+
s = STDIN.read
|
53
|
+
|
54
|
+
puts const_string(var_name, s)
|
data/bin/ritsu
CHANGED
@@ -1,32 +1,32 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# The command line Ritsu generator.
|
3
|
-
|
4
|
-
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
|
5
|
-
require 'ritsu'
|
6
|
-
require 'optparse'
|
7
|
-
|
8
|
-
options = {:generator => "default"}
|
9
|
-
OptionParser.new do |opts|
|
10
|
-
opts.banner = "Usage: ritsu PROJECT_NAME [OPTIONS]"
|
11
|
-
opts.on("-g", "--generator GENERATOR", "name of the generator to use") do |generator|
|
12
|
-
options[:generator] = generator
|
13
|
-
end
|
14
|
-
|
15
|
-
opts.parse!
|
16
|
-
if ARGV.length < 1
|
17
|
-
puts opts
|
18
|
-
exit
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
generator_class = Ritsu::ProjectGenerator.generator_classes[options[:generator]]
|
23
|
-
|
24
|
-
if generator_class.nil?
|
25
|
-
puts "there is no generator with name '#{options[:generator]}'"
|
26
|
-
else
|
27
|
-
begin
|
28
|
-
generator_class.new.generate(ARGV[0], '.', options)
|
29
|
-
rescue Exception => e
|
30
|
-
puts e
|
31
|
-
end
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# The command line Ritsu generator.
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
|
5
|
+
require 'ritsu'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
options = {:generator => "default"}
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: ritsu PROJECT_NAME [OPTIONS]"
|
11
|
+
opts.on("-g", "--generator GENERATOR", "name of the generator to use") do |generator|
|
12
|
+
options[:generator] = generator
|
13
|
+
end
|
14
|
+
|
15
|
+
opts.parse!
|
16
|
+
if ARGV.length < 1
|
17
|
+
puts opts
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
generator_class = Ritsu::ProjectGenerator.generator_classes[options[:generator]]
|
23
|
+
|
24
|
+
if generator_class.nil?
|
25
|
+
puts "there is no generator with name '#{options[:generator]}'"
|
26
|
+
else
|
27
|
+
begin
|
28
|
+
generator_class.new.generate(ARGV[0], '.', options)
|
29
|
+
rescue Exception => e
|
30
|
+
puts e
|
31
|
+
end
|
32
32
|
end
|
data/lib/ritsu.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'active_support'
|
3
|
-
|
4
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/external_library')
|
5
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/project')
|
6
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/target')
|
7
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/src_file')
|
8
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/block')
|
9
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/template')
|
10
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/project_generator')
|
11
|
-
|
12
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/template_policies')
|
13
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/targets')
|
14
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/src_files')
|
15
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/utility')
|
16
|
-
require File.expand_path(File.dirname(__FILE__) + '/ritsu/project_generators')
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support'
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/external_library')
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/project')
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/target')
|
7
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/src_file')
|
8
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/block')
|
9
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/template')
|
10
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/project_generator')
|
11
|
+
|
12
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/template_policies')
|
13
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/targets')
|
14
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/src_files')
|
15
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/utility')
|
16
|
+
require File.expand_path(File.dirname(__FILE__) + '/ritsu/project_generators')
|
17
17
|
require File.expand_path(File.dirname(__FILE__) + '/ritsu/thors')
|
data/lib/ritsu/block.rb
CHANGED
@@ -1,259 +1,259 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'active_support/core_ext/string/starts_ends_with'
|
3
|
-
require File.dirname(__FILE__) + '/utility/check_upon_add_set'
|
4
|
-
require File.dirname(__FILE__) + '/utility/files'
|
5
|
-
require File.dirname(__FILE__) + '/utility/strings'
|
6
|
-
|
7
|
-
module Ritsu
|
8
|
-
module BlockMixin
|
9
|
-
attr_reader :id
|
10
|
-
attr_accessor :contents
|
11
|
-
attr_accessor :local_indentation
|
12
|
-
attr_accessor :indent_level
|
13
|
-
attr_accessor :indent_length
|
14
|
-
|
15
|
-
def initialize_block_mixin(id = nil, options={})
|
16
|
-
options = {
|
17
|
-
:contents => [],
|
18
|
-
:local_indentation => "",
|
19
|
-
:indent_length=>4
|
20
|
-
}.merge(options)
|
21
|
-
|
22
|
-
@id = id
|
23
|
-
@contents = options[:contents]
|
24
|
-
@local_indentation = options[:local_indentation]
|
25
|
-
@indent_length = options[:indent_length]
|
26
|
-
@indent_level = 0
|
27
|
-
end
|
28
|
-
|
29
|
-
def add_line(line)
|
30
|
-
contents << " " * (indent_level * indent_length) + line
|
31
|
-
end
|
32
|
-
|
33
|
-
def add_new_line
|
34
|
-
add_line("")
|
35
|
-
end
|
36
|
-
|
37
|
-
protected
|
38
|
-
def add_block_structure(block)
|
39
|
-
block.local_indentation = block.local_indentation + " " * (indent_level * indent_length)
|
40
|
-
contents << block
|
41
|
-
end
|
42
|
-
|
43
|
-
def add_line_or_other_content(content)
|
44
|
-
if content.kind_of?(String)
|
45
|
-
add_line(content)
|
46
|
-
else
|
47
|
-
contents << content
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
public
|
52
|
-
def clear_contents
|
53
|
-
contents.clear
|
54
|
-
end
|
55
|
-
|
56
|
-
def indent
|
57
|
-
@indent_level += 1
|
58
|
-
end
|
59
|
-
|
60
|
-
def outdent
|
61
|
-
@indent_level -= 1
|
62
|
-
end
|
63
|
-
|
64
|
-
def block_structure?
|
65
|
-
true
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
class Block
|
70
|
-
include BlockMixin
|
71
|
-
|
72
|
-
attr_accessor :block_start_prefix
|
73
|
-
attr_accessor :block_end_prefix
|
74
|
-
|
75
|
-
def initialize(id = nil, options={})
|
76
|
-
options = {
|
77
|
-
:block_start_prefix => "//<<",
|
78
|
-
:block_end_prefix => "//>>"
|
79
|
-
}.merge(options)
|
80
|
-
|
81
|
-
@block_start_prefix = options[:block_start_prefix]
|
82
|
-
@block_end_prefix = options[:block_end_prefix]
|
83
|
-
|
84
|
-
initialize_block_mixin(id, options)
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.extract_block_id(str, prefix)
|
88
|
-
str.strip.slice((prefix.length)..-1).strip
|
89
|
-
end
|
90
|
-
|
91
|
-
def extract_block_id(str, prefix)
|
92
|
-
Block.extract_block_id(str, prefix)
|
93
|
-
end
|
94
|
-
|
95
|
-
def parse_lines(lines, options={})
|
96
|
-
options = {
|
97
|
-
:block_start_prefix => block_start_prefix,
|
98
|
-
:block_end_prefix => block_end_prefix
|
99
|
-
}.merge(options)
|
100
|
-
@block_start_prefix = options[:block_start_prefix]
|
101
|
-
@block_end_prefix = options[:block_end_prefix]
|
102
|
-
|
103
|
-
block_stack = [self]
|
104
|
-
global_indentation_length = 0
|
105
|
-
|
106
|
-
get_local_indentation = Proc.new do |line|
|
107
|
-
leading_spaces_length = Ritsu::Utility::Strings.leading_spaces(line, options).length
|
108
|
-
remaining_space_length = leading_spaces_length - global_indentation_length
|
109
|
-
if remaining_space_length < 0 then remaining_space_length = 0 end
|
110
|
-
" " * remaining_space_length
|
111
|
-
end
|
112
|
-
|
113
|
-
append_line = Proc.new do |line|
|
114
|
-
block_stack.last.contents << (get_local_indentation.call(line) + line.lstrip)
|
115
|
-
end
|
116
|
-
|
117
|
-
lines.each do |line|
|
118
|
-
if line.strip.starts_with?(block_start_prefix)
|
119
|
-
id = extract_block_id(line, block_start_prefix)
|
120
|
-
local_indentation = get_local_indentation.call(line)
|
121
|
-
|
122
|
-
options[:local_indentation] = local_indentation
|
123
|
-
block = Block.new(id, options)
|
124
|
-
block_stack.last.contents << block
|
125
|
-
block_stack.push(block)
|
126
|
-
global_indentation_length += block.local_indentation.length
|
127
|
-
elsif line.strip.starts_with?(block_end_prefix)
|
128
|
-
id = extract_block_id(line, block_end_prefix)
|
129
|
-
if block_stack.last.id == id
|
130
|
-
block = block_stack.pop()
|
131
|
-
global_indentation_length -= block.local_indentation.length
|
132
|
-
else
|
133
|
-
append_line.call(line)
|
134
|
-
end
|
135
|
-
else
|
136
|
-
append_line.call(line)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
if block_stack.length != 1
|
141
|
-
raise ArgumentError.new("error in input. some blocks are malformed")
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
def parse_string(string, options={})
|
146
|
-
lines = string.rstrip.split("\n")
|
147
|
-
parse_lines(lines, options)
|
148
|
-
end
|
149
|
-
|
150
|
-
def parse_file(filename, options={})
|
151
|
-
text = Ritsu::Utility::Files.read(filename)
|
152
|
-
parse_string(text, options)
|
153
|
-
end
|
154
|
-
|
155
|
-
##
|
156
|
-
# In all case, the generated string shall have no trailing whitespaces.
|
157
|
-
def to_s(options={})
|
158
|
-
options = {:no_delimiter => false, :indentation => ""}.merge(options)
|
159
|
-
no_delimiter = options.delete(:no_delimiter)
|
160
|
-
indentation = options.delete(:indentation)
|
161
|
-
|
162
|
-
io = StringIO.new
|
163
|
-
io << indentation + local_indentation + block_start_prefix + " " + id + "\n" unless no_delimiter
|
164
|
-
contents.each do |content|
|
165
|
-
if content.kind_of?(Block)
|
166
|
-
io << content.to_s({:indentation=>indentation+local_indentation}.merge(options)) + "\n"
|
167
|
-
else
|
168
|
-
io << indentation + local_indentation + content.to_s + "\n"
|
169
|
-
end
|
170
|
-
end
|
171
|
-
io << indentation + local_indentation + block_end_prefix + " " + id unless no_delimiter
|
172
|
-
|
173
|
-
io.string.rstrip
|
174
|
-
end
|
175
|
-
|
176
|
-
def write_to_file(filename, options={})
|
177
|
-
options = {:no_delimiter => true}.merge(options)
|
178
|
-
|
179
|
-
File.open(filename, "w") do |f|
|
180
|
-
f.write(to_s(options))
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def self.parse(*args)
|
185
|
-
if args.length > 2
|
186
|
-
raise ArgumentError.new("only at most 2 arguments, the second one is a hash, are accepted")
|
187
|
-
end
|
188
|
-
options = {}
|
189
|
-
|
190
|
-
prepare_options_from_kth_arg = Proc.new do |k|
|
191
|
-
options = options.merge(args[k]) if (args.length > k)
|
192
|
-
end
|
193
|
-
|
194
|
-
block = Block.new
|
195
|
-
result = case args[0]
|
196
|
-
when Array
|
197
|
-
prepare_options_from_kth_arg.call(1)
|
198
|
-
block.parse_lines(args[0], options)
|
199
|
-
when String
|
200
|
-
prepare_options_from_kth_arg.call(1)
|
201
|
-
block.parse_string(args[0], options)
|
202
|
-
when Hash
|
203
|
-
if filename = args[0].delete(:file)
|
204
|
-
prepare_options_from_kth_arg.call(0)
|
205
|
-
block.parse_file(filename, options)
|
206
|
-
else
|
207
|
-
raise ArgumentError.new("you must specify :file option if the first argument is a hash")
|
208
|
-
end
|
209
|
-
end
|
210
|
-
return block
|
211
|
-
end
|
212
|
-
|
213
|
-
def child_block_count
|
214
|
-
count = 0
|
215
|
-
contents.each do |content|
|
216
|
-
count += 1 if content.kind_of?(Block)
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
##
|
221
|
-
# @return (Block) the first child block with the given ID. nil if there is no such child block.
|
222
|
-
def child_block_with_id(id)
|
223
|
-
contents.each do |content|
|
224
|
-
if content.kind_of?(Block) and content.id == id
|
225
|
-
return content
|
226
|
-
end
|
227
|
-
end
|
228
|
-
return nil
|
229
|
-
end
|
230
|
-
|
231
|
-
def child_blocks
|
232
|
-
contents.select {|x| x.kind_of?(Block)}
|
233
|
-
end
|
234
|
-
|
235
|
-
##
|
236
|
-
# @return (Integer) the position of the child block with the given ID in the contents array.
|
237
|
-
# nil if there is no such child block.
|
238
|
-
def child_block_with_id_position(id)
|
239
|
-
contents.length.times do |i|
|
240
|
-
if contents[i].kind_of?(Block) and contents[i].id == id
|
241
|
-
return i
|
242
|
-
end
|
243
|
-
end
|
244
|
-
return nil
|
245
|
-
end
|
246
|
-
|
247
|
-
def add_block(block)
|
248
|
-
add_block_structure(block)
|
249
|
-
end
|
250
|
-
|
251
|
-
def add_content(content)
|
252
|
-
if content.kind_of?(Block)
|
253
|
-
add_block(content)
|
254
|
-
else
|
255
|
-
add_line_or_other_content(content)
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support/core_ext/string/starts_ends_with'
|
3
|
+
require File.dirname(__FILE__) + '/utility/check_upon_add_set'
|
4
|
+
require File.dirname(__FILE__) + '/utility/files'
|
5
|
+
require File.dirname(__FILE__) + '/utility/strings'
|
6
|
+
|
7
|
+
module Ritsu
|
8
|
+
module BlockMixin
|
9
|
+
attr_reader :id
|
10
|
+
attr_accessor :contents
|
11
|
+
attr_accessor :local_indentation
|
12
|
+
attr_accessor :indent_level
|
13
|
+
attr_accessor :indent_length
|
14
|
+
|
15
|
+
def initialize_block_mixin(id = nil, options={})
|
16
|
+
options = {
|
17
|
+
:contents => [],
|
18
|
+
:local_indentation => "",
|
19
|
+
:indent_length=>4
|
20
|
+
}.merge(options)
|
21
|
+
|
22
|
+
@id = id
|
23
|
+
@contents = options[:contents]
|
24
|
+
@local_indentation = options[:local_indentation]
|
25
|
+
@indent_length = options[:indent_length]
|
26
|
+
@indent_level = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_line(line)
|
30
|
+
contents << " " * (indent_level * indent_length) + line
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_new_line
|
34
|
+
add_line("")
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def add_block_structure(block)
|
39
|
+
block.local_indentation = block.local_indentation + " " * (indent_level * indent_length)
|
40
|
+
contents << block
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_line_or_other_content(content)
|
44
|
+
if content.kind_of?(String)
|
45
|
+
add_line(content)
|
46
|
+
else
|
47
|
+
contents << content
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
public
|
52
|
+
def clear_contents
|
53
|
+
contents.clear
|
54
|
+
end
|
55
|
+
|
56
|
+
def indent
|
57
|
+
@indent_level += 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def outdent
|
61
|
+
@indent_level -= 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def block_structure?
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Block
|
70
|
+
include BlockMixin
|
71
|
+
|
72
|
+
attr_accessor :block_start_prefix
|
73
|
+
attr_accessor :block_end_prefix
|
74
|
+
|
75
|
+
def initialize(id = nil, options={})
|
76
|
+
options = {
|
77
|
+
:block_start_prefix => "//<<",
|
78
|
+
:block_end_prefix => "//>>"
|
79
|
+
}.merge(options)
|
80
|
+
|
81
|
+
@block_start_prefix = options[:block_start_prefix]
|
82
|
+
@block_end_prefix = options[:block_end_prefix]
|
83
|
+
|
84
|
+
initialize_block_mixin(id, options)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.extract_block_id(str, prefix)
|
88
|
+
str.strip.slice((prefix.length)..-1).strip
|
89
|
+
end
|
90
|
+
|
91
|
+
def extract_block_id(str, prefix)
|
92
|
+
Block.extract_block_id(str, prefix)
|
93
|
+
end
|
94
|
+
|
95
|
+
def parse_lines(lines, options={})
|
96
|
+
options = {
|
97
|
+
:block_start_prefix => block_start_prefix,
|
98
|
+
:block_end_prefix => block_end_prefix
|
99
|
+
}.merge(options)
|
100
|
+
@block_start_prefix = options[:block_start_prefix]
|
101
|
+
@block_end_prefix = options[:block_end_prefix]
|
102
|
+
|
103
|
+
block_stack = [self]
|
104
|
+
global_indentation_length = 0
|
105
|
+
|
106
|
+
get_local_indentation = Proc.new do |line|
|
107
|
+
leading_spaces_length = Ritsu::Utility::Strings.leading_spaces(line, options).length
|
108
|
+
remaining_space_length = leading_spaces_length - global_indentation_length
|
109
|
+
if remaining_space_length < 0 then remaining_space_length = 0 end
|
110
|
+
" " * remaining_space_length
|
111
|
+
end
|
112
|
+
|
113
|
+
append_line = Proc.new do |line|
|
114
|
+
block_stack.last.contents << (get_local_indentation.call(line) + line.lstrip)
|
115
|
+
end
|
116
|
+
|
117
|
+
lines.each do |line|
|
118
|
+
if line.strip.starts_with?(block_start_prefix)
|
119
|
+
id = extract_block_id(line, block_start_prefix)
|
120
|
+
local_indentation = get_local_indentation.call(line)
|
121
|
+
|
122
|
+
options[:local_indentation] = local_indentation
|
123
|
+
block = Block.new(id, options)
|
124
|
+
block_stack.last.contents << block
|
125
|
+
block_stack.push(block)
|
126
|
+
global_indentation_length += block.local_indentation.length
|
127
|
+
elsif line.strip.starts_with?(block_end_prefix)
|
128
|
+
id = extract_block_id(line, block_end_prefix)
|
129
|
+
if block_stack.last.id == id
|
130
|
+
block = block_stack.pop()
|
131
|
+
global_indentation_length -= block.local_indentation.length
|
132
|
+
else
|
133
|
+
append_line.call(line)
|
134
|
+
end
|
135
|
+
else
|
136
|
+
append_line.call(line)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
if block_stack.length != 1
|
141
|
+
raise ArgumentError.new("error in input. some blocks are malformed")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def parse_string(string, options={})
|
146
|
+
lines = string.rstrip.split("\n")
|
147
|
+
parse_lines(lines, options)
|
148
|
+
end
|
149
|
+
|
150
|
+
def parse_file(filename, options={})
|
151
|
+
text = Ritsu::Utility::Files.read(filename)
|
152
|
+
parse_string(text, options)
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# In all case, the generated string shall have no trailing whitespaces.
|
157
|
+
def to_s(options={})
|
158
|
+
options = {:no_delimiter => false, :indentation => ""}.merge(options)
|
159
|
+
no_delimiter = options.delete(:no_delimiter)
|
160
|
+
indentation = options.delete(:indentation)
|
161
|
+
|
162
|
+
io = StringIO.new
|
163
|
+
io << indentation + local_indentation + block_start_prefix + " " + id + "\n" unless no_delimiter
|
164
|
+
contents.each do |content|
|
165
|
+
if content.kind_of?(Block)
|
166
|
+
io << content.to_s({:indentation=>indentation+local_indentation}.merge(options)) + "\n"
|
167
|
+
else
|
168
|
+
io << indentation + local_indentation + content.to_s + "\n"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
io << indentation + local_indentation + block_end_prefix + " " + id unless no_delimiter
|
172
|
+
|
173
|
+
io.string.rstrip
|
174
|
+
end
|
175
|
+
|
176
|
+
def write_to_file(filename, options={})
|
177
|
+
options = {:no_delimiter => true}.merge(options)
|
178
|
+
|
179
|
+
File.open(filename, "w") do |f|
|
180
|
+
f.write(to_s(options))
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.parse(*args)
|
185
|
+
if args.length > 2
|
186
|
+
raise ArgumentError.new("only at most 2 arguments, the second one is a hash, are accepted")
|
187
|
+
end
|
188
|
+
options = {}
|
189
|
+
|
190
|
+
prepare_options_from_kth_arg = Proc.new do |k|
|
191
|
+
options = options.merge(args[k]) if (args.length > k)
|
192
|
+
end
|
193
|
+
|
194
|
+
block = Block.new
|
195
|
+
result = case args[0]
|
196
|
+
when Array
|
197
|
+
prepare_options_from_kth_arg.call(1)
|
198
|
+
block.parse_lines(args[0], options)
|
199
|
+
when String
|
200
|
+
prepare_options_from_kth_arg.call(1)
|
201
|
+
block.parse_string(args[0], options)
|
202
|
+
when Hash
|
203
|
+
if filename = args[0].delete(:file)
|
204
|
+
prepare_options_from_kth_arg.call(0)
|
205
|
+
block.parse_file(filename, options)
|
206
|
+
else
|
207
|
+
raise ArgumentError.new("you must specify :file option if the first argument is a hash")
|
208
|
+
end
|
209
|
+
end
|
210
|
+
return block
|
211
|
+
end
|
212
|
+
|
213
|
+
def child_block_count
|
214
|
+
count = 0
|
215
|
+
contents.each do |content|
|
216
|
+
count += 1 if content.kind_of?(Block)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
##
|
221
|
+
# @return (Block) the first child block with the given ID. nil if there is no such child block.
|
222
|
+
def child_block_with_id(id)
|
223
|
+
contents.each do |content|
|
224
|
+
if content.kind_of?(Block) and content.id == id
|
225
|
+
return content
|
226
|
+
end
|
227
|
+
end
|
228
|
+
return nil
|
229
|
+
end
|
230
|
+
|
231
|
+
def child_blocks
|
232
|
+
contents.select {|x| x.kind_of?(Block)}
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# @return (Integer) the position of the child block with the given ID in the contents array.
|
237
|
+
# nil if there is no such child block.
|
238
|
+
def child_block_with_id_position(id)
|
239
|
+
contents.length.times do |i|
|
240
|
+
if contents[i].kind_of?(Block) and contents[i].id == id
|
241
|
+
return i
|
242
|
+
end
|
243
|
+
end
|
244
|
+
return nil
|
245
|
+
end
|
246
|
+
|
247
|
+
def add_block(block)
|
248
|
+
add_block_structure(block)
|
249
|
+
end
|
250
|
+
|
251
|
+
def add_content(content)
|
252
|
+
if content.kind_of?(Block)
|
253
|
+
add_block(content)
|
254
|
+
else
|
255
|
+
add_line_or_other_content(content)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
259
|
end
|