require 'pathname' require 'rake/clean' require 'open3' M2C = 'gm2' # Modula-2 compiler. stage_compiler = Pathname.new 'build/stage1/elna' directory 'build/stage1' directory 'build/source' directory 'build/self' CLEAN.include 'build' rule(/build\/stage1\/.+\.o$/ => ->(file) { path = Pathname.new('boot/stage1/source') + Pathname.new(file).basename ['build/stage1', path.sub_ext('.def'), path.sub_ext('.mod')] }) do |t| sources = t.prerequisites.filter { |f| f.end_with? '.mod' } sh M2C, '-c', '-I', 'boot/stage1/source', '-o', t.name, *sources end file 'build/stage1/elna' => FileList['boot/stage1/source/*'].map { |file| File.join 'build', 'stage1', Pathname.new(file).basename.sub_ext('.o') } do |t| sh M2C, '-o', t.name, *t.prerequisites end file 'build/stage1/Compiler.o' => ['build/stage1', 'boot/stage1/source/Compiler.mod'] do |t| sources = t.prerequisites.filter { |f| f.end_with? '.mod' } sh M2C, '-fscaffold-main', '-c', '-I', 'boot/stage1/source', '-o', t.name, *sources end ['source', 'self'].each do |sub| rule(/build\/#{sub}\/.+\.mod$/ => [ "build/#{sub}", stage_compiler.to_path, ->(file) { File.join('source', Pathname.new(file).basename.sub_ext('.elna')) } ]) do |t| sources, compiler = t.prerequisites .reject { |f| File.directory? f } .partition { |f| f.end_with? '.elna' } File.open t.name, 'w' do |output| compiler_command = compiler + sources puts puts(compiler_command * ' ') Open3.popen2(*compiler_command) do |cl_in, cl_out| cl_in.close IO.copy_stream cl_out, output cl_out.close end end end rule(/build\/#{sub}\/.+\.o$/ => ->(file) { path = Pathname.new(file).relative_path_from('build') result = [] result << File.join('source', path.basename.sub_ext('.def')) result << File.join('build', path.sub_ext('.mod')) }) do |t| sources = t.prerequisites.filter { |f| f.end_with? '.mod' } sh M2C, '-c', '-I', 'source', '-o', t.name, *sources end file "build/#{sub}/Compiler.o" => ["build/#{sub}/Compiler.mod"] do |t| sh M2C, '-fscaffold-main', '-c', '-I', 'source', '-o', t.name, *t.prerequisites end stage_compiler = Pathname.new('build') + sub + 'elna' file stage_compiler => FileList["source/*.elna"].map { |file| File.join 'build', sub, Pathname.new(file).basename.sub_ext('.o') } do |t| sh M2C, '-o', t.name, *t.prerequisites end end task default: 'build/self/elna' task default: 'build/self/Compiler.mod' task default: 'source/Compiler.elna' task :default do |t| exe, previous_output, source = t.prerequisites exe_arguments = [exe, source] diff_arguments = ['diff', '-Nur', '--text', previous_output, '-'] puts [exe, diff_arguments * ' '].join(' | ') Open3.pipeline exe_arguments, diff_arguments end task :backport do FileList['source/*.elna'].each do |file| source_path = Pathname.new file source = File.read source_path current_procedure = nil target = '' source .gsub(/^(var|type|const|begin)/) { |match| match.upcase } .gsub(/^[[:alnum:]]* ?module/) { |match| match.upcase } .gsub(/\b(record|nil|or|false|true)\b/) { |match| match.upcase } .gsub(/proc\(/, 'PROCEDURE(') .gsub(/ & /, ' AND ') .gsub(/ -> /, ': ') .gsub(/end\./, "END #{source_path.basename.sub_ext('')}.") .gsub(/([[:space:]]*)end(;?)$/, '\1END\2') .gsub(/^([[:space:]]*)(while|return|if)\b/) { |match| match.upcase } .gsub(/^from ([[:alnum:]]+) import/, 'FROM \1 IMPORT') .gsub(/ \^([[:alnum:]])/, ' POINTER TO \1') .gsub(/(then|do)$/) { |match| match.upcase } .gsub(/(:|=) \[([[:digit:]]+)\]/, '\1 ARRAY[1..\2] OF ') .each_line do |line| if line.start_with? 'proc' current_procedure = line[5...line.index('(')] line = 'PROCEDURE ' + line[5..].gsub(',', ';') elsif line.start_with?('END;') && !current_procedure.nil? line = "END #{current_procedure};" current_proceure = nil elsif line.start_with?('end') line = 'END ' + line[4..] end target += line end target_path = Pathname.new('boot/stage1/source') + source_path.basename.sub_ext('.mod') File.write target_path, target end FileList['source/*.def'].each do |file| cp file, File.join('boot/stage1/source', Pathname.new(file).basename) end end