Class: Korekto::Main

Inherits:
Object
  • Object
show all
Defined in:
lib/korekto/main.rb

Constant Summary collapse

MD_STATEMENT_CODE_TITLE =

rubocop: disable Layout/LineLength

%r{^(?<statement>.*)\s#(?<code>[A-Z](\d+(\.\w+)?(/[\w,.]+)?)?)(\s+(?<title>[^#]+))?$}
MD_FILENAME =
%r{^< (?<filename>[/\w\-.]+)$}
MD_KLASS_METHOD_DEFINITION =

patch

/^(?<klass>::[A-Z]\w+)#(?<method>\w+)(?<definition>[^=]*=.+)$/
MD_RULE =
/^[?] (?<rule>\S.*)$/
MD_TYPE_PATTERN =
%r{^! (?<type>\S+)\s+/(?<pattern>.*)/$}
MD_TYPE_VARIABLES =
/^! (?<type>\S+)\s+\{(?<variables>\S+( \S+)*)\}$/
MD_KEY_VALUE =
/^! (?<key>\w+):\s+'(?<value>.*)'$/
M_FENCE =

rubocop: enable Layout/LineLength

/^```\s*$/
M_COMMENT_LINE =
/^\s*#/
BACKUPS =
{}

Instance Method Summary collapse

Constructor Details

#initialize(filename = '-', statements: Statements.new, imports: []) ⇒ Main

Returns a new instance of Main.



18
19
20
21
22
23
24
# File 'lib/korekto/main.rb', line 18

def initialize(filename='-', statements: Statements.new, imports: [])
  @filename,@statements,@imports = filename,statements,imports
  @imports.push @filename.freeze
  @line,@active = nil,false
  @section = File.basename(@filename,'.*')
  @m_fence_korekto = /^```korekto$/ # default fence
end

Instance Method Details

#active?Boolean

Is the current line a non-comment Korekto line?

Returns:

  • (Boolean)


151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/korekto/main.rb', line 151

def active?
  case @line
  when @m_fence_korekto
    raise Error, 'unexpected fence' if @active
    @active = true
    false
  when M_FENCE
    @active = false
  else
    @active && !M_COMMENT_LINE.match?(@line)
  end
end

#key_value(key, value) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/korekto/main.rb', line 86

def key_value(key, value)
  case key
  when 'scanner'
    @statements.symbols.set_scanner value
  when 'fence'
    @m_fence_korekto = Regexp.new "^```#{value}$" # user defined fence
  when 'section'
    @section = value
  when 'save'
    BACKUPS[value] = Marshal.dump(@statements)
  when 'restore'
    if (backup = BACKUPS[value])
      @statements = Marshal.load(backup)
    else
      raise Error, "nothing saved as '#{value}'"
    end
  else
    raise Error, "key '#{key}' not implemented"
  end
end

#parse(lines) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/korekto/main.rb', line 107

def parse(lines)
  statement_number = line_number = 0
  while (@line = lines.shift)
    begin
      line_number += 1
      next unless active?
      next if preprocess?
      if (md = MD_STATEMENT_CODE_TITLE.match @line)
        code,title = @statements.add(md[:statement].strip,
                                     md[:code],
                                     md[:title],
                                     @section
                                      # Block executes if statement is new.
                                      # Method recieves the return value.
                                    ){ statement_number += 1 }
        if Korekto.scrape?
          print @statements.last
          print "\t##{code}"
          print " #{title}" unless title.nil? || title.empty?
          puts
        elsif Korekto.trace? ||
              (@filename=='-' && !(md[:code]==code && md[:title]==title))
          puts "#{@filename}:#{line_number}:#{code}:#{title}"
        end
      else
        raise Error, 'unrecognized korekto line'
      end
    rescue Error
      puts "#{@filename}:#{line_number}:!:#{$!.message}"
      exit 65
    rescue StandardError
      puts "#{@filename}:#{line_number}:?:#{$!.message}"
      warn $!.backtrace
      exit 1
    end
  end
end

#patch(klass, method, definition) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/korekto/main.rb', line 42

def patch(klass, method, definition)
  if Object.const_get(klass).method_defined?(method)
    raise Error, "overrides: #{klass}##{method}"
  end
  # rubocop: disable Security/Eval
  # rubocop: disable Style/DocumentDynamicEvalDefinition
  # rubocop: disable Style/EvalWithLocation
  eval <<~EVAL
    class #{klass}
      def #{method}#{definition}
    end
  EVAL
  # rubocop: enable Style/EvalWithLocation
  # rubocop: enable Style/DocumentDynamicEvalDefinition
  # rubocop: enable Security/Eval
end

#preprocess?Boolean

Returns:

  • (Boolean)


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/korekto/main.rb', line 59

def preprocess?
  case @line
  when MD_FILENAME
    filename = $~[:filename].strip
    unless @imports.include? filename
      Main.new(filename, statements:@statements, imports:@imports).run
    end
  when MD_KLASS_METHOD_DEFINITION
    if @filename=='-' && !Korekto.patch?
      # We'll trust files, but not stdin.
      raise Error, 'monkey patching not allowed on stdin'
    end
    patch($~[:klass],$~[:method],$~[:definition])
  when MD_RULE
    @statements.syntax.push $~[:rule].strip
  when MD_TYPE_PATTERN
    type_pattern($~[:type], $~[:pattern])
  when MD_TYPE_VARIABLES
    type_variables($~[:type], $~[:variables].split)
  when MD_KEY_VALUE
    key_value($~[:key], $~[:value])
  else
    return false
  end
  true
end

#runObject



145
146
147
148
# File 'lib/korekto/main.rb', line 145

def run
  parse @filename=='-' ? $stdin.readlines(chomp: true) :
                         File.readlines(@filename, chomp: true)
end

#type_pattern(type, pattern) ⇒ Object

Raises:



26
27
28
29
30
# File 'lib/korekto/main.rb', line 26

def type_pattern(type, pattern)
  t2p = @statements.symbols.t2p
  raise Error, "type #{type} in use" if t2p.key? type
  t2p[type] = pattern
end

#type_variables(type, variables) ⇒ Object

Raises:



32
33
34
35
36
37
38
39
40
# File 'lib/korekto/main.rb', line 32

def type_variables(type, variables)
  v2t,t2p = @statements.symbols.v2t,@statements.symbols.t2p
  pattern = t2p[type]
  raise Error, "type #{type} not defined" unless pattern
  variables.each do |variable|
    raise Error, "variable #{variable} in use" if v2t.key? variable
    v2t[variable] = type
  end
end