at_coder_friends 0.5.2 → 0.6.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +1 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/config/default.yml +3 -0
- data/docs/CONFIGURATION.md +74 -9
- data/lib/at_coder_friends.rb +10 -5
- data/lib/at_coder_friends/cli.rb +2 -5
- data/lib/at_coder_friends/context.rb +4 -0
- data/lib/at_coder_friends/emitter.rb +2 -2
- data/lib/at_coder_friends/generator/cxx_builtin.rb +191 -0
- data/lib/at_coder_friends/generator/main.rb +53 -0
- data/lib/at_coder_friends/generator/ruby_builtin.rb +128 -0
- data/lib/at_coder_friends/parser/binary.rb +39 -0
- data/lib/at_coder_friends/parser/constraints.rb +36 -0
- data/lib/at_coder_friends/parser/{format_parser.rb → input_format.rb} +42 -30
- data/lib/at_coder_friends/parser/interactive.rb +29 -0
- data/lib/at_coder_friends/parser/main.rb +6 -3
- data/lib/at_coder_friends/parser/sample_data.rb +24 -0
- data/lib/at_coder_friends/parser/section_wrapper.rb +49 -0
- data/lib/at_coder_friends/parser/{page_parser.rb → sections.rb} +44 -50
- data/lib/at_coder_friends/problem.rb +40 -24
- data/lib/at_coder_friends/scraping/agent.rb +1 -5
- data/lib/at_coder_friends/scraping/authentication.rb +2 -2
- data/lib/at_coder_friends/scraping/session.rb +1 -1
- data/lib/at_coder_friends/scraping/tasks.rb +2 -6
- data/lib/at_coder_friends/test_runner/base.rb +36 -31
- data/lib/at_coder_friends/test_runner/judge.rb +2 -6
- data/lib/at_coder_friends/test_runner/sample.rb +8 -6
- data/lib/at_coder_friends/version.rb +1 -1
- data/tasks/regression/check_diff.rake +29 -0
- data/tasks/regression/check_parse.rake +56 -0
- data/tasks/regression/regression.rb +67 -0
- data/tasks/regression/section_list.rake +41 -0
- data/tasks/regression/setup.rake +48 -0
- data/templates/cxx_builtin_default.cxx +26 -0
- data/templates/cxx_builtin_interactive.cxx +61 -0
- data/templates/ruby_builtin_default.rb +5 -0
- data/templates/ruby_builtin_interactive.rb +32 -0
- metadata +21 -8
- data/lib/at_coder_friends/cxx_generator.rb +0 -169
- data/lib/at_coder_friends/parser/constraints_parser.rb +0 -26
- data/lib/at_coder_friends/ruby_generator.rb +0 -97
- data/tasks/regression.rake +0 -163
| @@ -1,26 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module AtCoderFriends
         | 
| 4 | 
            -
              module Parser
         | 
| 5 | 
            -
                # parses constraints
         | 
| 6 | 
            -
                module ConstraintsParser
         | 
| 7 | 
            -
                  module_function
         | 
| 8 | 
            -
             | 
| 9 | 
            -
                  def process(pbm)
         | 
| 10 | 
            -
                    pbm.constraints = parse(pbm.desc)
         | 
| 11 | 
            -
                  end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                  def parse(desc)
         | 
| 14 | 
            -
                    desc
         | 
| 15 | 
            -
                      .gsub(/[,\\(){}|]/, '')
         | 
| 16 | 
            -
                      .gsub(/(≤|leq?)/i, '≦')
         | 
| 17 | 
            -
                      .scan(/([\da-z_]+)\s*≦\s*(\d+)(?:\^(\d+))?/i)
         | 
| 18 | 
            -
                      .map do |v, sz, k|
         | 
| 19 | 
            -
                        sz = sz.to_i
         | 
| 20 | 
            -
                        sz **= k.to_i if k
         | 
| 21 | 
            -
                        Constraint.new(v, :max, sz)
         | 
| 22 | 
            -
                      end
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
                end
         | 
| 25 | 
            -
              end
         | 
| 26 | 
            -
            end
         | 
| @@ -1,97 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module AtCoderFriends
         | 
| 4 | 
            -
              # generates C++ source code from definition
         | 
| 5 | 
            -
              class RubyGenerator
         | 
| 6 | 
            -
                TEMPLATE = <<~TEXT
         | 
| 7 | 
            -
                  ### DCLS ###
         | 
| 8 | 
            -
             | 
| 9 | 
            -
                  puts ans
         | 
| 10 | 
            -
                TEXT
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                def process(pbm)
         | 
| 13 | 
            -
                  src = generate(pbm.defs)
         | 
| 14 | 
            -
                  pbm.add_src(:rb, src)
         | 
| 15 | 
            -
                end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                def generate(defs)
         | 
| 18 | 
            -
                  dcls = gen_decls(defs).join("\n")
         | 
| 19 | 
            -
                  TEMPLATE.sub('### DCLS ###', dcls)
         | 
| 20 | 
            -
                end
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                def gen_decls(defs)
         | 
| 23 | 
            -
                  defs.map { |inpdef| gen_decl(inpdef) }.flatten
         | 
| 24 | 
            -
                end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                def gen_decl(inpdef)
         | 
| 27 | 
            -
                  case inpdef.container
         | 
| 28 | 
            -
                  when :single
         | 
| 29 | 
            -
                    gen_single_decl(inpdef)
         | 
| 30 | 
            -
                  when :harray
         | 
| 31 | 
            -
                    gen_harray_decl(inpdef)
         | 
| 32 | 
            -
                  when :varray
         | 
| 33 | 
            -
                    if inpdef.names.size == 1
         | 
| 34 | 
            -
                      gen_varray_1_decl(inpdef)
         | 
| 35 | 
            -
                    else
         | 
| 36 | 
            -
                      gen_varray_n_decl(inpdef)
         | 
| 37 | 
            -
                    end
         | 
| 38 | 
            -
                  when :matrix
         | 
| 39 | 
            -
                    gen_matrix_decl(inpdef)
         | 
| 40 | 
            -
                  end
         | 
| 41 | 
            -
                end
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                def gen_single_decl(inpdef)
         | 
| 44 | 
            -
                  names = inpdef.names
         | 
| 45 | 
            -
                  dcl = names.join(', ')
         | 
| 46 | 
            -
                  expr = gen_expr(inpdef.item, names.size > 1)
         | 
| 47 | 
            -
                  "#{dcl} = #{expr}"
         | 
| 48 | 
            -
                end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                def gen_harray_decl(inpdef)
         | 
| 51 | 
            -
                  v = inpdef.names[0]
         | 
| 52 | 
            -
                  dcl = "#{v}s"
         | 
| 53 | 
            -
                  expr = gen_expr(inpdef.item, true)
         | 
| 54 | 
            -
                  "#{dcl} = #{expr}"
         | 
| 55 | 
            -
                end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                def gen_varray_1_decl(inpdef)
         | 
| 58 | 
            -
                  v = inpdef.names[0]
         | 
| 59 | 
            -
                  sz = inpdef.size[0]
         | 
| 60 | 
            -
                  dcl = "#{v}s"
         | 
| 61 | 
            -
                  expr = gen_expr(inpdef.item, false)
         | 
| 62 | 
            -
                  "#{dcl} = Array.new(#{sz}) { #{expr} }"
         | 
| 63 | 
            -
                end
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                def gen_varray_n_decl(inpdef)
         | 
| 66 | 
            -
                  names = inpdef.names
         | 
| 67 | 
            -
                  sz = inpdef.size[0]
         | 
| 68 | 
            -
                  dcl = names.map { |v| "#{v}s[i]" }.join(', ')
         | 
| 69 | 
            -
                  expr = gen_expr(inpdef.item, true)
         | 
| 70 | 
            -
                  ret = []
         | 
| 71 | 
            -
                  ret += names.map { |v| "#{v}s = Array.new(#{sz})" }
         | 
| 72 | 
            -
                  ret << "#{sz}.times do |i|"
         | 
| 73 | 
            -
                  ret << "  #{dcl} = #{expr}"
         | 
| 74 | 
            -
                  ret << 'end'
         | 
| 75 | 
            -
                  ret
         | 
| 76 | 
            -
                end
         | 
| 77 | 
            -
             | 
| 78 | 
            -
                def gen_matrix_decl(inpdef)
         | 
| 79 | 
            -
                  v = inpdef.names[0]
         | 
| 80 | 
            -
                  sz = inpdef.size[0]
         | 
| 81 | 
            -
                  decl = "#{v}ss"
         | 
| 82 | 
            -
                  expr = gen_expr(inpdef.item, true)
         | 
| 83 | 
            -
                  "#{decl} = Array.new(#{sz}) { #{expr} }"
         | 
| 84 | 
            -
                end
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                def gen_expr(item, split)
         | 
| 87 | 
            -
                  case item
         | 
| 88 | 
            -
                  when :number
         | 
| 89 | 
            -
                    split ? 'gets.split.map(&:to_i)' : 'gets.to_i'
         | 
| 90 | 
            -
                  when :string
         | 
| 91 | 
            -
                    split ? 'gets.chomp.split' : 'gets.chomp'
         | 
| 92 | 
            -
                  when :char
         | 
| 93 | 
            -
                    'gets.chomp'
         | 
| 94 | 
            -
                  end
         | 
| 95 | 
            -
                end
         | 
| 96 | 
            -
              end
         | 
| 97 | 
            -
            end
         | 
    
        data/tasks/regression.rake
    DELETED
    
    | @@ -1,163 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'net/http'
         | 
| 4 | 
            -
            require 'uri'
         | 
| 5 | 
            -
            require 'json'
         | 
| 6 | 
            -
            require 'mechanize'
         | 
| 7 | 
            -
            require 'at_coder_friends'
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            module AtCoderFriends
         | 
| 10 | 
            -
              # tasks for regression test
         | 
| 11 | 
            -
              module Regression
         | 
| 12 | 
            -
                module_function
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                CONTEST_LIST_URL = 'https://kenkoooo.com/atcoder/resources/contests.json'
         | 
| 15 | 
            -
                ACF_HOME = File.realpath(File.join(__dir__, '..'))
         | 
| 16 | 
            -
                REGRESSION_HOME = File.join(ACF_HOME, 'regression')
         | 
| 17 | 
            -
                PAGES_DIR = File.join(REGRESSION_HOME, 'pages')
         | 
| 18 | 
            -
                EMIT_ORG_DIR = File.join(REGRESSION_HOME, 'emit_org')
         | 
| 19 | 
            -
                EMIT_DIR_FMT = File.join(REGRESSION_HOME, 'emit_%<now>s')
         | 
| 20 | 
            -
             | 
| 21 | 
            -
                def setup
         | 
| 22 | 
            -
                  rmdir_force(PAGES_DIR)
         | 
| 23 | 
            -
                  rmdir_force(EMIT_ORG_DIR)
         | 
| 24 | 
            -
                  contest_id_list.each do |contest|
         | 
| 25 | 
            -
                    begin
         | 
| 26 | 
            -
                      ctx = context(EMIT_ORG_DIR, contest)
         | 
| 27 | 
            -
                      ctx.scraping_agent.fetch_all do |pbm|
         | 
| 28 | 
            -
                        begin
         | 
| 29 | 
            -
                          html_path = File.join(PAGES_DIR, contest, "#{pbm.q}.html")
         | 
| 30 | 
            -
                          save_file(html_path, pbm.page.body)
         | 
| 31 | 
            -
                          pipeline(ctx, pbm)
         | 
| 32 | 
            -
                        rescue StandardError => e
         | 
| 33 | 
            -
                          p e
         | 
| 34 | 
            -
                        end
         | 
| 35 | 
            -
                      end
         | 
| 36 | 
            -
                    rescue StandardError => e
         | 
| 37 | 
            -
                      p e
         | 
| 38 | 
            -
                    end
         | 
| 39 | 
            -
                    sleep 3
         | 
| 40 | 
            -
                  end
         | 
| 41 | 
            -
                end
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                def check_diff
         | 
| 44 | 
            -
                  emit_dir = format(EMIT_DIR_FMT, now: Time.now.strftime('%Y%m%d%H%M%S'))
         | 
| 45 | 
            -
                  rmdir_force(emit_dir)
         | 
| 46 | 
            -
             | 
| 47 | 
            -
                  local_pbm_list.each do |contest, q, url|
         | 
| 48 | 
            -
                    ctx = context(emit_dir, contest)
         | 
| 49 | 
            -
                    pbm = ctx.scraping_agent.fetch_problem(q, url)
         | 
| 50 | 
            -
                    pipeline(ctx, pbm)
         | 
| 51 | 
            -
                  end
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                  system("diff -r #{EMIT_ORG_DIR} #{emit_dir}")
         | 
| 54 | 
            -
                end
         | 
| 55 | 
            -
             | 
| 56 | 
            -
                def section_list
         | 
| 57 | 
            -
                  agent = Mechanize.new
         | 
| 58 | 
            -
                  list = local_pbm_list.flat_map do |contest, q, url|
         | 
| 59 | 
            -
                    page = agent.get(url)
         | 
| 60 | 
            -
                    %w[h2 h3].flat_map do
         | 
| 61 | 
            -
                      page.search('h3').map do |h3|
         | 
| 62 | 
            -
                        { contest: contest, q: q, text: normalize(h3.content) }
         | 
| 63 | 
            -
                      end
         | 
| 64 | 
            -
                    end
         | 
| 65 | 
            -
                  end
         | 
| 66 | 
            -
                  list.group_by { |sec| sec[:text] }.each do |k, vs|
         | 
| 67 | 
            -
                    puts [k, vs.size, vs[0][:contest], vs[0][:q]].join("\t")
         | 
| 68 | 
            -
                  end
         | 
| 69 | 
            -
                end
         | 
| 70 | 
            -
             | 
| 71 | 
            -
                def check_parse
         | 
| 72 | 
            -
                  parse_list
         | 
| 73 | 
            -
                    .select { |_, _, no_fmt, no_smp| no_fmt || no_smp }
         | 
| 74 | 
            -
                    .map { |c, q, no_fmt, no_smp| [c, q, f_to_s(no_fmt), f_to_s(no_smp)] }
         | 
| 75 | 
            -
                    .sort
         | 
| 76 | 
            -
                    .each { |args| puts args.join("\t") }
         | 
| 77 | 
            -
                end
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                def parse_list
         | 
| 80 | 
            -
                  local_pbm_list.map do |contest, q, url|
         | 
| 81 | 
            -
                    ctx = context('', contest)
         | 
| 82 | 
            -
                    pbm = ctx.scraping_agent.fetch_problem(q, url)
         | 
| 83 | 
            -
                    Parser::Main.process(pbm)
         | 
| 84 | 
            -
                    no_fmt = pbm.fmt.empty?
         | 
| 85 | 
            -
                    no_smp = pbm.smps.all? { |smp| smp.txt.empty? }
         | 
| 86 | 
            -
                    [contest, q, no_fmt, no_smp]
         | 
| 87 | 
            -
                  end
         | 
| 88 | 
            -
                end
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                def f_to_s(f)
         | 
| 91 | 
            -
                  f ? '◯' : '-'
         | 
| 92 | 
            -
                end
         | 
| 93 | 
            -
             | 
| 94 | 
            -
                def contest_id_list
         | 
| 95 | 
            -
                  uri = URI.parse(CONTEST_LIST_URL)
         | 
| 96 | 
            -
                  json = Net::HTTP.get(uri)
         | 
| 97 | 
            -
                  contests = JSON.parse(json)
         | 
| 98 | 
            -
                  puts "Total #{contests.size} contests"
         | 
| 99 | 
            -
                  contests.map { |h| h['id'] }
         | 
| 100 | 
            -
                end
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                def local_pbm_list
         | 
| 103 | 
            -
                  Dir.glob(PAGES_DIR + '/**/*.html').map do |pbm_path|
         | 
| 104 | 
            -
                    contest = File.basename(File.dirname(pbm_path))
         | 
| 105 | 
            -
                    q = File.basename(pbm_path, '.html')
         | 
| 106 | 
            -
                    url = "file://#{pbm_path}"
         | 
| 107 | 
            -
                    [contest, q, url]
         | 
| 108 | 
            -
                  end
         | 
| 109 | 
            -
                end
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                def context(root, contest)
         | 
| 112 | 
            -
                  Context.new({}, File.join(root, contest))
         | 
| 113 | 
            -
                end
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                def rmdir_force(dir)
         | 
| 116 | 
            -
                  FileUtils.rm_r(dir) if Dir.exist?(dir)
         | 
| 117 | 
            -
                end
         | 
| 118 | 
            -
             | 
| 119 | 
            -
                def save_file(path, content)
         | 
| 120 | 
            -
                  dir = File.dirname(path)
         | 
| 121 | 
            -
                  FileUtils.makedirs(dir) unless Dir.exist?(dir)
         | 
| 122 | 
            -
                  File.binwrite(path, content)
         | 
| 123 | 
            -
                end
         | 
| 124 | 
            -
             | 
| 125 | 
            -
                def pipeline(ctx, pbm)
         | 
| 126 | 
            -
                  @rb_gen ||= RubyGenerator.new
         | 
| 127 | 
            -
                  @cxx_gen ||= CxxGenerator.new
         | 
| 128 | 
            -
                  Parser::Main.process(pbm)
         | 
| 129 | 
            -
                  @rb_gen.process(pbm)
         | 
| 130 | 
            -
                  @cxx_gen.process(pbm)
         | 
| 131 | 
            -
                  ctx.emitter.emit(pbm)
         | 
| 132 | 
            -
                end
         | 
| 133 | 
            -
             | 
| 134 | 
            -
                def normalize(s)
         | 
| 135 | 
            -
                  s
         | 
| 136 | 
            -
                    .tr(' 0-9A-Za-z', ' 0-9A-Za-z')
         | 
| 137 | 
            -
                    .gsub(/[^一-龠_ぁ-ん_ァ-ヶーa-zA-Z0-9 ]/, '')
         | 
| 138 | 
            -
                    .strip
         | 
| 139 | 
            -
                end
         | 
| 140 | 
            -
              end
         | 
| 141 | 
            -
            end
         | 
| 142 | 
            -
             | 
| 143 | 
            -
            namespace :regression do
         | 
| 144 | 
            -
              desc 'setup regression environment'
         | 
| 145 | 
            -
              task :setup do
         | 
| 146 | 
            -
                AtCoderFriends::Regression.setup
         | 
| 147 | 
            -
              end
         | 
| 148 | 
            -
             | 
| 149 | 
            -
              desc 'run regression check'
         | 
| 150 | 
            -
              task :check_diff do
         | 
| 151 | 
            -
                AtCoderFriends::Regression.check_diff
         | 
| 152 | 
            -
              end
         | 
| 153 | 
            -
             | 
| 154 | 
            -
              desc 'list all section titles'
         | 
| 155 | 
            -
              task :section_list do
         | 
| 156 | 
            -
                AtCoderFriends::Regression.section_list
         | 
| 157 | 
            -
              end
         | 
| 158 | 
            -
             | 
| 159 | 
            -
              desc 'checks page parse result'
         | 
| 160 | 
            -
              task :check_parse do
         | 
| 161 | 
            -
                AtCoderFriends::Regression.check_parse
         | 
| 162 | 
            -
              end
         | 
| 163 | 
            -
            end
         |