File: commander/entry.rb

Overview
Module Structure
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: Umu#8
  module: Commander#10
has properties
function: input (1/2) / 1 #17
function: input (2/E) / 1 #22
constant: STDIN_FILE_NAME #30
constant: PARSER #32
function: main / 1 #35
function: setup / 1 #82
function: interact / 2 #102
function: process_line / 4 #178
function: process_files / 2 #259
function: process_source / 4 #276
function: process_tokens / 2 #331
function: execute / 2 #374
function: print_trace_of_con_syntax / 2 #394
function: print_trace_of_abs_syntax / 2 #413

Code

   1  # coding: utf-8
   2  # frozen_string_literal: true
   3 
   4  require 'pp'
   5 
   6 
   7 
   8  module Umu
   9 
  10  module Commander
  11 
  12  module_function
  13 
  14  begin
  15      require 'readline'
  16 
  17      def input(prompt)
  18          Readline.readline(prompt, true)
  19      end
  20 
  21  rescue ::LoadError
  22      def input(prompt)
  23          STDERR.print prompt; STDERR.flush
  24 
  25          STDIN.gets
  26      end
  27  end
  28 
  29 
  30      STDIN_FILE_NAME = '<stdin>'
  31 
  32      PARSER = CS::Parser.new
  33 
  34 
  35      def main(args)
  36          ASSERT.kind_of args, ::Array
  37 
  38          exit_code = begin
  39              setup_env, file_names = Commander.setup args
  40 
  41              if setup_env.pref.interactive_mode?
  42                  env = unless file_names.empty?
  43                              Commander.process_files file_names, setup_env
  44                          else
  45                              setup_env
  46                          end
  47 
  48                  Commander.interact env, setup_env
  49              else
  50                  if file_names.empty?
  51                      raise X::CommandError.new('No input files')
  52                  end
  53 
  54                  Commander.process_files file_names, setup_env
  55              end
  56 
  57              0
  58          rescue X::Abstraction::RuntimeError => e
  59              e.print_backtrace
  60              STDERR.puts
  61              STDERR.puts e.to_s
  62 
  63              1
  64          rescue X::Abstraction::Expected => e
  65              STDERR.puts
  66              STDERR.puts e.to_s
  67 
  68              1
  69          rescue ::SystemCallError, ::IOError, ::SystemStackError => e
  70              STDERR.puts
  71              STDERR.printf "[%s] %s\n", e.class.to_s, e.to_s
  72 
  73              1
  74          rescue ::Interrupt
  75              1
  76          end
  77 
  78          ASSERT.kind_of exit_code, ::Integer
  79      end
  80 
  81 
  82      def setup(args)
  83          pref, file_names = Commander.parse_option(
  84                                  args, E::INITIAL_PREFERENCE
  85                              )
  86 
  87          env = if pref.no_prelude?
  88                      E.setup pref
  89                  else
  90                      Commander.process_source(
  91                          Prelude::SOURCE_TEXT,
  92                          Prelude::FILE_NAME,
  93                          E.setup(E::INITIAL_PREFERENCE),
  94                          Prelude::START_LINE_NUM
  95                      ).update_preference pref
  96                  end
  97 
  98          [env, file_names]
  99      end
 100 
 101 
 102      def interact(init_env, setup_env)
 103          ASSERT.kind_of init_env,    E::Entry
 104          ASSERT.kind_of setup_env,   E::Entry
 105 
 106          init_tokens = []
 107          init_lexer  = LL.make_initial_lexer STDIN_FILE_NAME, 0
 108          final_env = loop.inject(
 109               [init_tokens, init_lexer, init_env]
 110          ) { |(tokens,      lexer,      env     ), _|
 111              ASSERT.kind_of tokens,      ::Array
 112              ASSERT.kind_of lexer,       LL::Abstract
 113              ASSERT.kind_of env,         E::Entry
 114 
 115              line_num = lexer.loc.line_num
 116 
 117              prompt = format("umu:%d%s ",
 118                  line_num + 1,
 119 
 120                  if lexer.in_comment?
 121                      '#'
 122                  elsif lexer.in_braket?
 123                      '*'
 124                  else
 125                      '>'
 126                  end
 127              )
 128 
 129              next_tokens, next_lexer, next_env = begin
 130                  opt_line = Commander.input prompt
 131                  break env if opt_line.nil?
 132                  line = opt_line
 133 
 134                  if lexer.between_braket? && /^:/ =~ line
 135                      [
 136                          [],
 137                          lexer.next_line_num,
 138                          Subcommand.execute(line, line_num, env, setup_env)
 139                      ]
 140                  else
 141                      Commander.process_line(
 142                          line + "\n",
 143                          tokens,
 144                          lexer,
 145                          env.update_line(STDIN_FILE_NAME, line_num, line)
 146                      )
 147                  end
 148              rescue X::Abstraction::RuntimeError => e
 149                  e.print_backtrace
 150                  STDERR.puts
 151                  STDERR.puts e.to_s
 152 
 153                  [[], lexer.recover, env]
 154              rescue X::Abstraction::Expected => e
 155                  STDERR.puts
 156                  STDERR.puts e.to_s
 157 
 158                  [[], lexer.recover, env]
 159              rescue ::SystemCallError, ::IOError, ::SystemStackError => e
 160                  STDERR.puts
 161                  STDERR.printf "[%s] %s\n", e.class.to_s, e.to_s
 162 
 163                  [[], lexer.recover, env]
 164              rescue ::Interrupt
 165                  STDERR.puts
 166                  STDERR.puts '^C'
 167 
 168                  [[], lexer.recover, env]
 169              end
 170 
 171              [next_tokens, next_lexer, next_env]
 172          }
 173 
 174          ASSERT.kind_of final_env, E::Entry
 175      end
 176 
 177 
 178      def process_line(line, tokens, init_lexer, env)
 179          ASSERT.kind_of line,        ::String
 180          ASSERT.kind_of tokens,      ::Array
 181          ASSERT.kind_of init_lexer,  LL::Abstract
 182          ASSERT.kind_of env,         E::Entry
 183 
 184          pref        = env.pref
 185          file_name   = STDIN_FILE_NAME
 186          line_num    = init_lexer.loc.line_num
 187 
 188          if pref.any_trace?
 189              STDERR.printf "________ Source: '%s' ________\n", file_name
 190              STDERR.printf "%04d: %s", line_num + 1, line
 191          end
 192 
 193          if pref.lex_trace_mode?
 194              STDERR.puts
 195              STDERR.printf "________ Lexer Trace: '%s' ________", file_name
 196              STDERR.printf "\nINIT-LEXTER: %s\n", init_lexer.to_s
 197          end
 198 
 199          scanner = ::StringScanner.new line
 200          next_tokens, next_lexer = LL.lex(
 201              tokens, init_lexer, scanner, pref
 202          ) do |event, matched, output_tokens, lexer, before_line_num|
 203 
 204              if pref.lex_trace_mode?
 205                  STDERR.printf("\nPATTERN: %s \"%s\"\n",
 206                                  event.to_s,
 207                                  Escape.unescape(matched)
 208                  )
 209                  unless output_tokens.empty?
 210                      STDERR.printf("    TOKENS: %s -- %s\n",
 211                                  output_tokens.map(&:to_s).join(' '),
 212                                  output_tokens[0].loc.to_s
 213                      )
 214                  end
 215                  STDERR.printf "    NEXT-LEXER: %s\n", lexer.to_s
 216              end
 217          end
 218 
 219          if next_lexer.between_braket?
 220              if pref.dump_mode?
 221                  STDERR.puts
 222                  STDERR.printf("________ Tokens: '%s' ________\n",
 223                                  file_name
 224                  )
 225                  STDERR.printf("%04d: %s\n",
 226                                  line_num + 1,
 227                                  next_tokens.map(&:to_s).join(' ')
 228                  )
 229              end
 230 
 231              next_env = Commander.process_tokens(next_tokens, env) do
 232                  |sym, value|
 233 
 234                  next if /^%/ =~ sym.to_s
 235 
 236                  case value
 237                  when VC::Fun
 238                      STDERR.printf "fun %s = ", sym.to_s
 239                  when VC::Struct::Entry
 240                      STDERR.printf "structure %s = ", sym.to_s
 241                  else
 242                      STDERR.printf("val %s : %s = ",
 243                                       sym.to_s,
 244                                       value.type_sym.to_s
 245                                  )
 246                  end
 247                  STDERR.flush
 248                  PP.pp value, STDERR
 249                  STDOUT.flush
 250              end
 251 
 252              [[],            next_lexer, next_env]
 253          else
 254              [next_tokens,   next_lexer, env]
 255          end
 256      end
 257 
 258 
 259      def process_files(file_names, init_env)
 260          ASSERT.kind_of file_names,  ::Array
 261          ASSERT.kind_of init_env,    E::Entry
 262 
 263          final_env = file_names.inject(init_env) { |env, file_name|
 264              ASSERT.kind_of env,         E::Entry
 265              ASSERT.kind_of file_name,   ::String
 266 
 267              source = ::File.open(file_name) { |io| io.read }
 268 
 269              Commander.process_source source, file_name, env
 270          }
 271 
 272          ASSERT.kind_of final_env, E::Entry
 273      end
 274 
 275 
 276      def process_source(source, file_name, env, init_line_num = 0)
 277          ASSERT.kind_of source,          ::String
 278          ASSERT.kind_of file_name,       ::String
 279          ASSERT.kind_of env,             E::Entry
 280          ASSERT.kind_of init_line_num,   ::Integer
 281 
 282          pref = env.pref
 283 
 284          if pref.dump_mode?
 285              STDERR.puts
 286              STDERR.printf "________ Source: '%s' ________\n", file_name
 287              source.each_line.with_index do |line, index|
 288                  STDERR.printf "%04d: %s", index + 1, line
 289              end
 290          end
 291 
 292          if pref.dump_mode?
 293              STDERR.puts
 294              STDERR.printf "________ Tokens: '%s' ________", file_name
 295          end
 296 
 297          init_tokens = []
 298          init_lexer  = LL.make_initial_lexer file_name, init_line_num
 299          scanner     = ::StringScanner.new source
 300          tokens, _lexer = LL.lex(
 301              init_tokens, init_lexer, scanner, pref
 302          ) do |_event, _matched, output_tokens, _lexer, before_line_num|
 303 
 304              if pref.dump_mode?
 305                  output_tokens.each do |token|
 306                      tk_line_num = token.loc.line_num
 307 
 308                      if tk_line_num != before_line_num
 309                          STDERR.printf "\n%04d: ", tk_line_num + 1
 310                      end
 311 
 312                      unless token && token.separator?
 313                          STDERR.printf "%s ", token.to_s
 314                          STDERR.flush
 315                      end
 316                  end
 317              end
 318          end
 319 
 320          if pref.dump_mode?
 321              STDERR.puts
 322          end
 323 
 324          Commander.process_tokens(
 325              tokens,
 326              env.update_source(file_name, source, init_line_num)
 327          )
 328      end
 329 
 330 
 331      def process_tokens(tokens, init_env)
 332          ASSERT.kind_of tokens,      ::Array
 333          ASSERT.kind_of init_env,    E::Entry
 334 
 335          csyn_stmts = PARSER.parse tokens
 336 
 337          final_env = csyn_stmts.inject(init_env) { |env, csyn_stmt|
 338              ASSERT.kind_of env,         E::Entry
 339              ASSERT.kind_of csyn_stmt,   CS::Abstract
 340 
 341              result = execute(csyn_stmt, env)
 342              ASSERT.kind_of result, ASR::Abstract
 343 
 344              case result
 345              when ASR::Value
 346                  value = result.value
 347 
 348                  if block_given?
 349                      yield :it, value
 350                  end
 351 
 352                  next_env = env.va_extend_value :it, value
 353              when ASR::Environment
 354                  next_env = result.env
 355 
 356                  if block_given?
 357                      next_env.va_get_bindings_difference_with(env).each do
 358                          |sym, value|
 359 
 360                          yield sym, value
 361                      end
 362                  end
 363 
 364                  next_env
 365              else
 366                  ASSERT.abort result.inspect
 367              end
 368          }
 369 
 370          ASSERT.kind_of final_env, E::Entry
 371      end
 372 
 373 
 374      def execute(csyn, env)
 375          ASSERT.kind_of env,     E::Entry
 376          ASSERT.kind_of csyn,    CS::Abstract
 377 
 378          print_trace_of_con_syntax csyn, env.pref
 379 
 380          asyn = csyn.desugar env
 381 
 382          print_trace_of_abs_syntax asyn, env.pref
 383 
 384          result = asyn.evaluate env
 385 
 386          if env.pref.any_trace?
 387              STDERR.puts
 388          end
 389 
 390          ASSERT.kind_of result, ASR::Abstract
 391      end
 392 
 393 
 394      def print_trace_of_con_syntax(csyn, pref)
 395          ASSERT.kind_of csyn,    CS::Abstract
 396 
 397          if pref.dump_mode?
 398              STDERR.puts
 399              STDERR.printf(
 400                  "________ Concrete Syntax: %s ________\n",
 401                  csyn.loc.to_s
 402              )
 403              PP.pp csyn, STDERR
 404          end
 405 
 406          if pref.trace_mode?
 407              STDERR.puts
 408              STDERR.puts "________ Desugar Trace ________"
 409          end
 410      end
 411 
 412 
 413      def print_trace_of_abs_syntax(asyn, pref)
 414          ASSERT.kind_of asyn,    AS::Abstract
 415 
 416          if pref.dump_mode?
 417              STDERR.puts
 418              STDERR.printf(
 419                  "________ Abstract Syntax: %s ________\n",
 420                  asyn.loc.to_s
 421              )
 422              PP.pp asyn, STDERR
 423          end
 424 
 425          if pref.trace_mode?
 426              STDERR.puts
 427              STDERR.puts "________ Evaluator Trace ________"
 428          end
 429      end
 430 
 431  end # Umu::Commander
 432 
 433  end # Umu