몇 가지 루비 예제 코드

루비를 제대로 공부하진 않았지만, 언어가 간결한 덕분에 여러가지 상황에서 유용하게 사용하고 있습니다. 그 동안 만들어서 사용했던 몇 가지 루비 예제 코드를 소개합니다. 여기에 소개하는 코드를 조금만 수정하면 다양한 경우에 활용할 수 있을 것 같네요.

1. 소스코드에서 32자 이상의 심볼을 사용한 라인을 찾아 출력하기

HITACHI 메인프레임 C 컴파일러가 32자 이상의 심볼을 지원하지 않더군요. 일일이 찾기 힘들어서 이를 찾기 위해 작성한 프로램입니다. 현재 디렉토리에 존재하는 소스코드를 읽어 32자 이상의 심볼을 찾은 경우 해당 라인을 라인번호와 함께 출력합니다.

file_array = Array.new

Dir.foreach(“.”) { |x|
  if x.include?(“.c”) or x.include?(“.h”) or x.include?(“.y”) or x.include?(“.l”)
    file_array.push(x)
  end
}

file_array.each { |fname|
  File.open(fname) { |fp|
    lineno = 1
    while line = fp.gets
        line.scan(/[1-9a-zA-Z_]+/) { |x|
          if x.length > 32
            puts “#{fname}:#{lineno}:+#{x.length – 32}:#{x}”
          end           
        }
        lineno = lineno + 1
    end
  }
}

2. 파일의 라인 뒤집기

매년 말 블로그에 독서 리스트를 정리할 때 사용하기 위해 작성한 프로그램입니다. 티스토리에서 목록을 뽑아 파일에 저장한 후 이를 뒤집어 독서 리스트를 읽은 순서대로 뽑아냅니다.

line_array = Array.new
File.open(“ReadingList.txt”) do |file|
    while line = file.gets
        line_array.push(line)
    end
end
ofile = File.new(“ReadingList.rev.txt”, “w”)
line_array.reverse!
cnt = 1
line_array.each do |reversed_line|
    ofile.puts(“#{cnt}. #{reversed_line}”)
    cnt = cnt + 1
end
ofile.close

3. 프로그램 수행 및 stderr, stdout 얻기

루비에서 다른 프로그램을 실행하고 stderr, stdout을 추출하는 예제 코드입니다. 루비를 사용하여 배치 스크립트를 작성 할 때 유용할 것 같네요.

require ‘session’

t=Thread.new do
  sh = Session.new
  sh.execute( ‘ruby /home/stefano/documenti/scripts/prova.rb’ ) do |out, err|
    puts “Msg: #{out}” if out
    puts “Err: #{err}” if err
  end
end
t.join

Ruby로 작성한 회귀 테스트 장치

저희팀(TmaxSoft, Compiler팀)은 컴파일러를 개발하고 있습니다. 컴파일러를 개발하다보면 코드의 수정 혹은 추가로 인해 기존에 잘 되던 것이 잘 안되는 문제가 빈번히 발생합니다. 언어를 처리하는 프로그램의 특성상 상호의존적인 코드의 비중이 높기 때문이죠.

그런 까닭에 “실용주의 프로그래머“에서도 강조하는 회귀 테스트가 정합성을 생명으로하는 컴파일러의 개발과정에서 빼놓을 수 없는 영역을 차지하게 됩니다. 

저희팀에서 컴파일러 혹은 인터프리터의 개발과정에서 사용하는, Ruby로 작성된 회귀 테스트 장치(regression test harness) 코드는 다음과 같습니다. (Ruby의 맛만 살짝 본 상태에서 제가 작성한 조악한 코드지만, ‘이런식으로 회귀 테스트를 하기도 하는구나!’ 정도로 이해해주시면 좋겠네요.)

컴파일러나 인터프리터가 제대로 동작하는지 확인하는 가장 간단한(?) 방법은 소스코드를 사용하여 예상대로 동작하는지 확인하는 것 입니다. 일반적인 경우에는 standard output(이하 stdout) 결과를 보고 이상유무를 파악합니다. 기본적인 아이디어는 여기서 정리하고 본론으로 들어가자면…

여기서 소개한 회귀 테스트를 위해서는 2가지 작업이 선행되어야 합니다.

1. 회귀 테스트에 포함시킬 예제 파일의 이름(확장자 제외)을 list.txt에 추가 (newline으로 여러개의 파일구분)
2. 정상 동작할때 stdout을 filename.out 파일에 저장 (e.g. ezp -i filename.ezt > filename.out)

회귀 테스트 과정은 다음과 같습니다.

1. list.txt에서 테스트 할 파일 이름을 추출하여 nameArray에 저장, 이 때 존재하는 파일인지 확인
2. ezp 인터프리터 실행하여 stdout을 filename.tmp에 저장
3. diff로 정상 동작시 결과 filename.out과 현재 실행 결과 filename.tmp를 비교
4. diff의 stdout이 비어 있으면 테스트 성공! 비어있지 않으면 failArray에 추가
5. 회귀테스트 결과 출력

저희팀에서는 (당연한 이야기겠지만) 저장소에 commit하기 전에 회귀 테스트를 통과하는 것을 정책적으로 강제하고 있습니다.

Ruby로 처음 작성한 코드, test harness

펄, 파이선, 루비 등의 스크립트 언어 중에 하나 정도는 알아 두는게 편할 것 같다는 생각에 이번 달에는 루비를 공부하고 있다. 책의 절반을 쭉 읽어 나가면서 코드는 한번도 작성한 적이 없었지만 언어가 간결해서 그런지 몰라도 쉽게 원하는 코드를 작성할 수 있었다.

지금 진행하고 있는 프로젝트는 코볼 컴파일러의 개발인데, 컴파일러가 생성한 코드가 정확한가를 확인하는 가장 쉬운 방법은 기존의 컴파일러가 생성한 코드와 수행 결과(stdout)을 비교하는 것.

지금까지는 급한 마음에 두 실행파일을 번갈아 실행하며 눈으로 수행결과를 비교했는데, “실용주의 프로그래머”를 보면 테스트를 포함한 프로젝트 빌드의 전과정을 자동으로 수행할 수 있도록 할 것을 강조하고 있다.

그리하여 고즈넉한 저녁에 잠깐의 짬과 약간의 용기를 내어 처음으로 루비를 사용해 간단한 test harness를 작성해 보았다.

name_list = [
    ‘intrinsic_math_func’,
    ‘intrinsic_char_func’,
    ‘intrinsic_case_func’,
    ‘intrinsic_value_func’,
    ‘intrinsic_divide_func’,
    ‘intrinsic_numval’,
    ‘intrinsic_annuity’
]

test_cnt = 0
succ_cnt = 0
fail_cnt = 0

# TODO
# compare execution time
name_list.each do |name|

    test_cnt += 1

    mf_exec = “./” + name + “.cob32”
    tmax_exec = “./” + name + “.gcobol”

    mf_stdout = `#{mf_exec}`
    tmax_stdout = `#{tmax_exec}`

    print “[#{test_cnt}] #{mf_exec} vs #{tmax_exec}”
    if (mf_stdout == tmax_stdout)
        puts ” …success”
        succ_cnt += 1
    else
        puts ” …fail”
        fail_cnt += 1
        puts “[#{mf_exec}]”
        puts mf_stdout
        puts “[#{tmax_exec}]”
        puts tmax_stdout
    end
    puts
end

puts
puts “Total : #{test_cnt}”
puts “Success : #{succ_cnt}”
puts “Fail : #{fail_cnt}”
puts

컴파일러가 생성한 코드의 수행시간 역시 컴파일러를 평가하는데 중요한 이슈이므로, 두가지 컴파일러가 생성한 코드의 수행시간을 자동으로 비교해 주는 프로그램을 루비로 간단히 작성하는 것도 큰 의미가 있을 것 같다. 사용하기 쉬운 루비 언어를 조금 더 연습해서 프로젝트의 여기저기에 잘 활용한다면 생산성을 향상 시킬 수 있을 것이다.