Class: Rubex::Compiler

Inherits:
Object
  • Object
show all
Defined in:
lib/rubex/compiler.rb

Constant Summary collapse

CONFIG =
Rubex::CompilerConfig.new

Class Method Summary collapse

Class Method Details

.ast(path, test: false, source_dir: nil) ⇒ Object

Generate the AST from Rubex source code. Do not use this function unless

contributing to Rubex. Use CLI or rake tasks instead.

Parameters:

  • path (String)

    Full path name of the rubex file.

  • test (Boolean) (defaults to: false)

    false Set to true if compiling rubex files for a test case.

  • source_dir (String) (defaults to: nil)

    nil Path of the directory that contains the source files.



71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rubex/compiler.rb', line 71

def ast path, test: false, source_dir: nil
  parser = Rubex::Parser.new
  parser.parse(path, source_dir, false)
  parser.do_parse
rescue Racc::ParseError => e
  raise e if test

  error_msg = "\nPARSE ERROR:\n"
  error_msg << "Line: #{parser.string.split("\n")[parser.lineno-1]}\n"
  error_msg << "Location: #{parser.location}\n"
  error_msg << "Error:\n#{e}"
  STDERR.puts error_msg
end

.build_path(directory, target_name) ⇒ Object

Create build path for outputting .c and .h files. Will check whether the calling

process is a rake task or cmd.

Parameters:

  • directory (String)

    The directory inside which this build path will exist.

  • target_name (String)

    Name of the folder inside the directory.



155
156
157
158
159
160
161
# File 'lib/rubex/compiler.rb', line 155

def build_path directory, target_name
  directory = (directory ? directory.to_s : Dir.pwd)
 unless directory.end_with?(target_name)
   directory += "/#{target_name}"
 end
  directory
end

.compile(path, test: false, multi_file: false, target_dir: nil, force: false, make: false, debug: false, source_dir: nil, files: nil) ⇒ Object

Compile the Rubex file(s) into .c file(s). Do not use this function directly

unless you're contributing to Rubex. Use the Rake tasks or command line
interface instead.

TODO: The path can be relative to the source_dir if source_dir is specified.

Parameters:

  • path (String)

    Full path to file. Main file in case of multiple file compilation.

  • test (Boolean) (defaults to: false)

    false Set to true if compiling rubex files for a test case.

  • multi_file (Boolean) (defaults to: false)

    false If true, return the CodeSupervisor object for code analysis by user. Applicable only if test: opt is true.

  • directory (String)

    nil Directory where the compiled rubex files should be placed.

  • force (Boolean) (defaults to: false)

    false If set to true, will forcefully overwrite any .c files that were previously generated.

  • make (Boolean) (defaults to: false)

    false If true, will automatically generate the .so file from compiled C binaries.

  • debug (Boolean) (defaults to: false)

    false If true, will compile C programs with gcc using the -g option.

  • source_dir (String) (defaults to: nil)

    nil String specifying the source directory inside which the source Rubex files will be placed in case of multi-file programs.

  • files (Array[String]) (defaults to: nil)

    nil An Array specifying the file names to compile w.r.t the source_dir directory.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/rubex/compiler.rb', line 32

def compile path,
            test: false,
            multi_file: false,
            target_dir: nil,
            force: false,
            make: false,
            debug: false,
            source_dir: nil,
            files: nil
  CONFIG.flush
  CONFIG.debug = debug
  CONFIG.add_link "m" # link cmath libraries
  target_name = extract_target_name path
  CONFIG.add_dep target_name
  
  tree = ast path, source_dir: source_dir, test: test
  supervisor = generate_code tree, target_name
  ext = extconf target_name, target_dir: target_dir
  
  if test && !multi_file
    return [tree, supervisor.code(target_name), ext,
            supervisor.header(target_name)]
  elsif test && multi_file
    return [tree, supervisor, ext]
  end
  write_files target_name, supervisor, ext, target_dir: target_dir, force: force
  full_path = build_path(target_dir, target_name)
  load_extconf full_path
  run_make full_path if make
end

.extconf(target_name, target_dir: nil) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/rubex/compiler.rb', line 85

def extconf target_name, target_dir: nil
  path = build_path(target_dir, target_name)
  extconf = ""
  extconf << "require 'mkmf'\n"
  extconf << "$libs += \" #{CONFIG.link_flags}\"\n"
  extconf << "$CFLAGS += \" -g \"\n" if CONFIG.debug
  
  srcs_config = CONFIG.srcs.map { |s| "\"#{s}\""}.join(',')
  extconf << "$srcs = [#{srcs_config}]\n"

  objs_config = CONFIG.objs.map { |o| "\"#{o}\""}.join(',')
  extconf << "$objs = [#{objs_config}]\n"
  
  extconf << "create_makefile('#{path}/#{target_name}')\n"
  extconf
end

.extract_target_name(path) ⇒ Object



111
112
113
# File 'lib/rubex/compiler.rb', line 111

def extract_target_name path
  File.basename(path).split('.')[0]
end

.generate_code(tree, target_name) ⇒ Object



102
103
104
105
106
107
108
109
# File 'lib/rubex/compiler.rb', line 102

def generate_code tree, target_name
  supervisor = Rubex::CodeSupervisor.new
  supervisor.init_file(target_name)
  raise "Must be a Rubex::AST::Node::MainNode, not #{tree.class}" unless
    tree.is_a? Rubex::AST::Node::MainNode
  tree.process_statements target_name, supervisor
  supervisor
end

.load_extconf(path) ⇒ Object



138
139
140
141
142
# File 'lib/rubex/compiler.rb', line 138

def load_extconf path
  Dir.chdir(path) do
    system("ruby #{path}/extconf.rb")
  end
end

.run_make(path) ⇒ Object



144
145
146
147
148
# File 'lib/rubex/compiler.rb', line 144

def run_make path
  Dir.chdir(path) do
    system("make -C #{path}")
  end
end

.write_files(target_name, supervisor, ext, target_dir: nil, force: false) ⇒ Object

Write .c and .h files from the generated C code from rubex files.

Parameters:

  • target_name (String)

    Target name of the root file.

  • supervisor (Rubex::CodeSupervisor)

    Container for code.

  • ext (String)

    A String representing the extconf file.

  • directory (String)

    nil Target directory in which files are to be placed.

  • force (Boolean) (defaults to: false)

    false Recreate the target directory and rewrite the files whether they are already present or not.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/rubex/compiler.rb', line 123

def write_files target_name, supervisor, ext, target_dir: nil, force: false
  path = build_path(target_dir, target_name)
  FileUtils.rm_rf(path) if force && Dir.exist?(path)
  Dir.mkdir(path) unless Dir.exist?(path)

  write_to_file "#{path}/#{Rubex::COMMON_UTILS_FILE}.h",
                supervisor.header(Rubex::COMMON_UTILS_FILE).to_s
  supervisor.files.each do |file|
    write_to_file "#{path}/#{file}.c", supervisor.code(file).to_s
    write_to_file "#{path}/#{file}.h", supervisor.header(file).to_s
  end
  
  write_to_file "#{path}/extconf.rb", ext
end