ritsu 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|