korekto 1.6.210409 → 2.0.231231

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91b2307d9115580d8a6882a7bf8d417d1eca426d7539fff91dce791ea7dd1ff9
4
- data.tar.gz: 1a1db1ac36359058341083031abe61e6e9c3258fdaf2cb2388140ef3e179b8fa
3
+ metadata.gz: 3b6d303ce45a7dc14c9dd65923f46d5c78728a7f90b5fa177cacce76a08e032f
4
+ data.tar.gz: 780b4c3d11b968a6183fe285e00c8d67b976691465694aa3ded19bde58e81d35
5
5
  SHA512:
6
- metadata.gz: 0bbcd36ec3471eb628d1274e013d2c9f6e8b85ab4e8b0d99a226a43d58cbe471a74f9b39a5db0c63e66af888353fa7be0069224fb35df4a80d75b148356fccc9
7
- data.tar.gz: ca4b3a0d970d6463f49f18af87767301d376d9a102031a88ebe3c8707114e60161f4ba4014e2608fda377515500b608361178b8b9ea9d02d3f577daa5e069c91
6
+ metadata.gz: 78b31ad18094000a9de76669b6d7fac178fa547b715e41705267e75c459fdf8e99b5d94ce310526f3991c9e3f5db9d6c92189d4a369bfd33df77b0c1bbd12969
7
+ data.tar.gz: 1efc9551d93da2507a736d725c5c043b4b710ced992b3d419cc0ee4cf5a10fa85dfd3700be81dd336869cb19808d8fc84d5b7482fb7604cff0d8cc96fe6e5754
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Korekto
2
+
3
+ * [VERSION 2.0.231231](https://github.com/carlosjhr64/korekto/releases)
4
+ * [github](https://www.github.com/carlosjhr64/korekto)
5
+ * [rubygems](https://rubygems.org/gems/korekto)
6
+
7
+ ## DESCRIPTION:
8
+
9
+ A general proof checker.
10
+
11
+ Works with [neovim](https://github.com/neovim/neovim).
12
+
13
+ ## INSTALL:
14
+ ```shell
15
+ $ gem install korekto
16
+ $ korekto --install
17
+ $ ### And if missing:
18
+ $ gem install neovim # Provides neovim-ruby-host
19
+ ```
20
+ ## SYNOPSIS:
21
+ ```korekto
22
+ ### Patterns ###
23
+ # 'Hello World!'.scan(/\w+|\S|\s/) #=> ["Hello", " ", "World", "!"]
24
+ ! scanner: '\w+|\S|\s'
25
+ ! .Newline /\n/
26
+ ! .Newline {;}
27
+ ! Variables /\w+/
28
+ ! Variables {V W}
29
+ ### Acceptance patterns ###
30
+ There might be V. #L1 Let 1: There might be .
31
+ # /If I see (\w+), then I'll probably see (\w+).\nI see \1\nI'll probably see \2/
32
+ If I see V, then I'll probably see W.;I see V.;I'll probably see W. #I2 Modus ponens: If I see , then ' ll probably
33
+ ### Argument ###
34
+ There might be Cows. #S3/L1 Let 1: Cows
35
+ There might be Chickens. #S4/L1 Let 1: Chickens
36
+ If I see Cows, then I'll probably see Chickens. #P5
37
+ I see Cows. #P6
38
+ I'll probably see Chickens. #C7/I2,P5,P6 Modus ponens
39
+ ```
40
+ ## Examples
41
+
42
+ * [Tutorial](examples/Tutorial.md)
43
+ * [ABC music notation](examples/ABC.md)
44
+ * [Computation](examples/Computation.md)
45
+ * [Dx x^x](examples/Dxx.md)
46
+ * [Sqrt(2) is irrational! (Classic Proof)](examples/Sqrt2.md)
47
+ * [Squash Function](examples/Squash.md)
48
+
49
+ ## Help:
50
+ ```shell
51
+ $ korekto --help
52
+ Usage:
53
+ korekto [:options+]
54
+ Options:
55
+ -h --help
56
+ -v --version
57
+ --scrape Scrape Korekto lines
58
+ --trace Show trace of each line, not just edits and errors
59
+ --patch Allow monkey patching in stdin
60
+ --install Installs the korekto neovim ruby plugin
61
+ --readme Open korekto github page
62
+ --heap=SIZE Set heap size (default: 13)
63
+ Types:
64
+ SIZE /^\d+$/
65
+ Exclusive:
66
+ scrape trace install readme
67
+ # Example usage:
68
+ # cat MARKDOWN.md | korekto
69
+ # korekto < MARKDOWN.md
70
+ ```
71
+ ## LICENSE:
72
+
73
+ Copyright (c) 2023 CarlosJHR64
74
+
75
+ Permission is hereby granted, free of charge,
76
+ to any person obtaining a copy of this software and
77
+ associated documentation files (the "Software"),
78
+ to deal in the Software without restriction,
79
+ including without limitation the rights
80
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
81
+ copies of the Software, and
82
+ to permit persons to whom the Software is furnished to do so,
83
+ subject to the following conditions:
84
+
85
+ The above copyright notice and this permission notice
86
+ shall be included in all copies or substantial portions of the Software.
87
+
88
+ THE SOFTWARE IS PROVIDED "AS IS",
89
+ WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
90
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
91
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
92
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
93
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
94
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
95
+ THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/bin/korekto CHANGED
@@ -1,38 +1,62 @@
1
1
  #!/usr/bin/env ruby
2
- require 'help_parser'
3
-
4
- OPTIONS = HelpParser['1.6.210409', <<HELP]
5
- Usage:
6
- korekto [:options]
7
- Options:
8
- -h --help
9
- -v --version
10
- --edits \tShow only needed edits
11
- --install \tInstalls the korekto neovim ruby plugin
12
- --readme \tOpen korekto github page
13
- # Example usage:
14
- # cat MARKDOWN.md | korekto
15
- # korekto < MARKDOWN.md
16
- HELP
2
+ require 'korekto'
17
3
 
18
- if OPTIONS.install?
4
+ def do_install
5
+ local_pack = File.expand_path '~/.local/share/nvim/site/pack'
6
+ config_nvim = File.expand_path '~/.config/nvim'
7
+ unless File.directory?(local_pack) && File.directory?(config_nvim)
8
+ warn "Expected directories:\n\t#{local_pack}\n\t#{config_nvim}"
9
+ exit 78 # EX_CONFIG
10
+ end
19
11
  require 'fileutils'
20
12
  # pack
21
- dest = File.expand_path '~/.local/share/nvim/site/pack/korekto'
13
+ dest = File.join(local_pack, 'korekto')
22
14
  FileUtils.mkdir_p dest
23
15
  src = File.join File.dirname(__dir__), 'start'
24
16
  FileUtils.cp_r src, dest
25
17
  # rplugin
26
- dest = File.expand_path '~/.config/nvim'
18
+ dest = config_nvim
27
19
  src = File.join File.dirname(__dir__), 'rplugin'
28
20
  FileUtils.cp_r src, dest
29
21
  system "nvim -c ':UpdateRemotePlugins|:quit' > /dev/null"
30
22
  exit
31
23
  end
32
- if OPTIONS.readme?
33
- Process.detach spawn('xdg-open', 'https://www.github.com/carlosjhr64/korekto')
24
+
25
+ def spawn_readme
26
+ Process.detach spawn('xdg-open', 'https://www.github.com/carlosjhr64/korekto',
27
+ %i[out err] => '/dev/null')
34
28
  exit
35
29
  end
36
30
 
37
- require 'korekto'
31
+ def handle_options
32
+ require 'help_parser'
33
+ options = HelpParser[Korekto::VERSION, <<~HELP]
34
+ Usage:
35
+ korekto [:options+]
36
+ Options:
37
+ -h --help
38
+ -v --version
39
+ --scrape \tScrape Korekto lines
40
+ --trace \tShow trace of each line, not just edits and errors
41
+ --patch \tAllow monkey patching in stdin
42
+ --install \tInstalls the korekto neovim ruby plugin
43
+ --readme \tOpen korekto github page
44
+ --heap=SIZE \tSet heap size (default: #{Korekto.heap})
45
+ Types:
46
+ SIZE /^\\d+$/
47
+ Exclusive:
48
+ scrape trace install readme
49
+ # Example usage:
50
+ # cat MARKDOWN.md | korekto
51
+ # korekto < MARKDOWN.md
52
+ HELP
53
+ do_install if options.install?
54
+ spawn_readme if options.readme?
55
+ Korekto.scrape = options.scrape?
56
+ Korekto.trace = options.trace?
57
+ Korekto.patch = options.patch?
58
+ Korekto.heap = options.heap.to_i if options.heap?
59
+ end
60
+
61
+ handle_options unless ARGV.empty?
38
62
  Korekto.run
data/lib/korekto/main.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  module Korekto
2
2
  class Main
3
- MD_STATEMENT_CODE_TITLE = %r{^(?<statement>.*)\s#(?<code>[A-Z](\d+(\.\w+)?(/[\w,.]+)?)?)( (?<title>\S.*?\S))?$}
3
+ # rubocop: disable Layout/LineLength
4
+ MD_STATEMENT_CODE_TITLE = %r{^(?<statement>.*)\s#(?<code>[A-Z](\d+(\.\w+)?(/[\w,.]+)?)?)(\s+(?<title>[^#]+))?$}
4
5
  MD_FILENAME = %r{^< (?<filename>[/\w\-.]+)$}
5
6
  MD_KLASS_METHOD_DEFINITION = /^(?<klass>::[A-Z]\w+)#(?<method>\w+)(?<definition>[^=]*=.+)$/ # patch
6
7
  MD_RULE = /^[?] (?<rule>\S.*)$/
7
- MD_TYPE_PATTERN = %r{^! (?<type>\S+)\s*/(?<pattern>.*)/$}
8
- MD_TYPE_VARIABLES = /^! (?<type>\S+)\s*\{(?<variables>\S+( \S+)*)\}$/
9
- MD_KEY_VALUE = /^! (?<key>\w+):\s*'(?<value>.*)'$/
8
+ MD_TYPE_PATTERN = %r{^! (?<type>\S+)\s+/(?<pattern>.*)/$}
9
+ MD_TYPE_VARIABLES = /^! (?<type>\S+)\s+\{(?<variables>\S+( \S+)*)\}$/
10
+ MD_KEY_VALUE = /^! (?<key>\w+):\s+'(?<value>.*)'$/
11
+ # rubocop: enable Layout/LineLength
10
12
 
11
13
  M_FENCE = /^```\s*$/
12
14
  M_COMMENT_LINE = /^\s*#/
@@ -18,12 +20,12 @@ class Main
18
20
  @imports.push @filename.freeze
19
21
  @line,@active = nil,false
20
22
  @section = File.basename(@filename,'.*')
21
- @m_fence_korekto = /^```korekto$/
23
+ @m_fence_korekto = /^```korekto$/ # default fence
22
24
  end
23
25
 
24
26
  def type_pattern(type, pattern)
25
27
  t2p = @statements.symbols.t2p
26
- raise Error, "type #{type} in use" if t2p.has_key? type
28
+ raise Error, "type #{type} in use" if t2p.key? type
27
29
  t2p[type] = pattern
28
30
  end
29
31
 
@@ -32,53 +34,26 @@ class Main
32
34
  pattern = t2p[type]
33
35
  raise Error, "type #{type} not defined" unless pattern
34
36
  variables.each do |variable|
35
- raise Error, "variable #{variable} in use" if v2t.has_key? variable
37
+ raise Error, "variable #{variable} in use" if v2t.key? variable
36
38
  v2t[variable] = type
37
39
  end
38
40
  end
39
41
 
40
- def active?
41
- case @line
42
- when @m_fence_korekto
43
- raise Error, 'unexpected fence' if @active
44
- @active = true
45
- false
46
- when M_FENCE
47
- @active = false
48
- false
49
- else
50
- @active and not M_COMMENT_LINE.match?(@line)
51
- end
52
- end
53
-
54
42
  def patch(klass, method, definition)
55
- raise Error, "overrides: #{klass}##{method}" if eval(klass).method_defined? method
43
+ if Object.const_get(klass).method_defined?(method)
44
+ raise Error, "overrides: #{klass}##{method}"
45
+ end
46
+ # rubocop: disable Security/Eval
47
+ # rubocop: disable Style/DocumentDynamicEvalDefinition
48
+ # rubocop: disable Style/EvalWithLocation
56
49
  eval <<~EVAL
57
50
  class #{klass}
58
51
  def #{method}#{definition}
59
52
  end
60
53
  EVAL
61
- end
62
-
63
- def key_value(key, value)
64
- case key
65
- when 'scanner'
66
- @statements.symbols.set_scanner value
67
- when 'fence'
68
- @m_fence_korekto = Regexp.new "^```#{value}$"
69
- when 'section'
70
- @section = value
71
- when 'save'
72
- BACKUPS[value] = Marshal.dump(@statements)
73
- when 'restore'
74
- if backup = BACKUPS[value]
75
- @statements = Marshal.load(backup)
76
- else
77
- raise Error, "nothing saved as '#{value}'"
78
- end
79
- else
80
- raise Error, "key '#{key}' not implemented"
81
- end
54
+ # rubocop: enable Style/EvalWithLocation
55
+ # rubocop: enable Style/DocumentDynamicEvalDefinition
56
+ # rubocop: enable Security/Eval
82
57
  end
83
58
 
84
59
  def preprocess?
@@ -89,6 +64,10 @@ class Main
89
64
  Main.new(filename, statements:@statements, imports:@imports).run
90
65
  end
91
66
  when MD_KLASS_METHOD_DEFINITION
67
+ if @filename=='-' && !Korekto.patch?
68
+ # We'll trust files, but not stdin.
69
+ raise Error, 'monkey patching not allowed on stdin'
70
+ end
92
71
  patch($~[:klass],$~[:method],$~[:definition])
93
72
  when MD_RULE
94
73
  @statements.syntax.push $~[:rule].strip
@@ -104,19 +83,49 @@ class Main
104
83
  true
105
84
  end
106
85
 
86
+ def key_value(key, value)
87
+ case key
88
+ when 'scanner'
89
+ @statements.symbols.set_scanner value
90
+ when 'fence'
91
+ @m_fence_korekto = Regexp.new "^```#{value}$" # user defined fence
92
+ when 'section'
93
+ @section = value
94
+ when 'save'
95
+ BACKUPS[value] = Marshal.dump(@statements)
96
+ when 'restore'
97
+ if (backup = BACKUPS[value])
98
+ @statements = Marshal.load(backup)
99
+ else
100
+ raise Error, "nothing saved as '#{value}'"
101
+ end
102
+ else
103
+ raise Error, "key '#{key}' not implemented"
104
+ end
105
+ end
106
+
107
107
  def parse(lines)
108
108
  statement_number = line_number = 0
109
- while @line = lines.shift
109
+ while (@line = lines.shift)
110
110
  begin
111
111
  line_number += 1
112
112
  next unless active?
113
113
  next if preprocess?
114
- if md = MD_STATEMENT_CODE_TITLE.match(@line)
114
+ if (md = MD_STATEMENT_CODE_TITLE.match @line)
115
115
  code,title = @statements.add(md[:statement].strip,
116
116
  md[:code],
117
117
  md[:title],
118
- @section){ statement_number += 1 }
119
- if not OPTIONS.edits? or (@filename=='-' and not (md[:code]==code and md[:title]==title))
118
+ @section
119
+ # Block executes if statement is new.
120
+ # Method recieves the return value.
121
+ ){ statement_number += 1 }
122
+ if Korekto.scrape?
123
+ print @statements.last
124
+ print "\t##{code}"
125
+ print " #{title}" unless title.nil? || title.empty?
126
+ puts
127
+ elsif Korekto.trace? ||
128
+ (@filename=='-' && !(md[:code]==code && md[:title]==title))
120
129
  puts "#{@filename}:#{line_number}:#{code}:#{title}"
121
130
  end
122
131
  else
@@ -125,19 +134,30 @@ class Main
125
134
  rescue Error
126
135
  puts "#{@filename}:#{line_number}:!:#{$!.message}"
127
136
  exit 65
128
- rescue Exception
137
+ rescue StandardError
129
138
  puts "#{@filename}:#{line_number}:?:#{$!.message}"
130
- $stderr.puts $!.backtrace
139
+ warn $!.backtrace
131
140
  exit 1
132
141
  end
133
142
  end
134
143
  end
135
144
 
136
145
  def run
137
- if @filename=='-'
138
- parse $stdin.readlines(chomp: true)
146
+ parse @filename=='-' ? $stdin.readlines(chomp: true) :
147
+ File.readlines(@filename, chomp: true)
148
+ end
149
+
150
+ # Is the current line a non-comment Korekto line?
151
+ def active?
152
+ case @line
153
+ when @m_fence_korekto
154
+ raise Error, 'unexpected fence' if @active
155
+ @active = true
156
+ false
157
+ when M_FENCE
158
+ @active = false
139
159
  else
140
- parse IO.readlines(@filename, chomp: true)
160
+ @active && !M_COMMENT_LINE.match?(@line)
141
161
  end
142
162
  end
143
163
  end
@@ -1,21 +1,26 @@
1
1
  module Korekto
2
2
  class Statement
3
3
  attr_reader :code,:title,:regexp,:section,:statement_number
4
+
5
+ # rubocop: disable Metrics/ParameterLists
4
6
  def initialize(statement,code,title,section,statement_number,context)
5
- @statement,@code,@title,@section,@statement_number,@context =
6
- statement,code,title,section,statement_number,context
7
+ @statement,@code,@title,@section,@statement_number,@context,@regexp =
8
+ statement,code,title,section,statement_number,context,nil
7
9
  @statement.freeze; @section.freeze; @statement_number.freeze
8
- syntax_check unless @statement[0]=='/' and @statement[-1]=='/' and ['A','L','M','E','I'].include?(@code[0])
9
- @regexp = nil
10
+ @title = @title.split(':',2).first if @title
11
+ syntax_check unless @statement[0]=='/' &&
12
+ @statement[-1]=='/' &&
13
+ %w[A L M E I].include?(@code[0])
10
14
  set_acceptance_code
11
15
  @code.freeze; @title.freeze; @regexp.freeze
12
16
  end
17
+ # rubocop: enable Metrics/ParameterLists
13
18
 
14
19
  def type = @code[0]
15
20
  def to_s = @statement
16
21
  def to_str = @statement
17
22
  def match?(statement) = @regexp.match?(statement)
18
- def scan(regex, &blk) = @statement.scan(regex, &blk)
23
+ def scan(regex, &) = @statement.scan(regex, &)
19
24
  def pattern? = !@regexp.nil?
20
25
  def literal_regexp? = @statement[0]=='/' && @statement[-1]=='/'
21
26
 
@@ -23,8 +28,10 @@ class Statement
23
28
 
24
29
  def syntax_check
25
30
  @context.syntax.each do |rule|
26
- raise Error, "syntax: #{rule}" unless @statement.instance_eval(rule)
27
- rescue # other than Korekto::Error < Exception
31
+ next if @statement.instance_eval(rule)
32
+ raise Error, "syntax: #{rule}"
33
+ rescue StandardError
34
+ raise if $!.is_a? Error
28
35
  raise Error, "#{$!.class}: #{rule}"
29
36
  end
30
37
  end
@@ -46,7 +53,7 @@ class Statement
46
53
  when 'T'
47
54
  tautology
48
55
  when 'A', 'L'
49
- # Axiom=>Tautoloty, Let=>Set,
56
+ # Axiom=>Tautology, Let=>Set,
50
57
  pattern_type(0)
51
58
  when 'M', 'E'
52
59
  # Map=>Result, Existential=>Instantiation(X)
@@ -55,14 +62,12 @@ class Statement
55
62
  # Inference=>Conclusion
56
63
  pattern_type(2)
57
64
  when 'W'
58
- ['T','S','R','X','C'].any? do |code|
59
- begin
60
- @code[0]=code
61
- set_acceptance_code
62
- true
63
- rescue Error
64
- false
65
- end
65
+ %w[T S R X C].any? do |code|
66
+ @code[0]=code
67
+ set_acceptance_code
68
+ true
69
+ rescue Error
70
+ false
66
71
  end or raise Error, 'did not match any statement pattern'
67
72
  else
68
73
  raise Error, "statement type #{@code[0]} not implemented"
@@ -71,26 +76,22 @@ class Statement
71
76
 
72
77
  # Common helper
73
78
 
74
- def set_statement(support=nil, title=nil)
79
+ def set_statement(support=nil, title=nil, undefined:nil)
75
80
  @code = "#{@code[0]}#{@statement_number}"
76
81
  @code += '.' + @section unless @section=='-'
77
82
  @code += "/#{support}" if support
78
- @title = title if (title=title&.split(':',2)&.first) and not title.empty?
83
+ @title = title if (title=title&.split(':',2)&.first) && !title.empty?
84
+ @title = "#{@title}: #{undefined.join(' ')}" if undefined
79
85
  end
80
86
 
81
- def support(*s)
82
- support = []
83
- s.each do |s|
84
- c = s.code.split('/',2)[0]
85
- support.push(c)
86
- end
87
- return support.join(',')
88
- end
87
+ def support(*s) = s.inject([]){|a,s| a.push(s.code.split('/',2)[0])}.join(',')
89
88
 
90
89
  # Pattern helpers
91
90
 
92
91
  def newlines_count(n)
93
- raise Error, "expected #{n} newlines" unless n==@regexp.inspect.gsub('\\\\','').scan('\\n').length
92
+ unless n==@regexp.inspect.gsub('\\\\','').scan('\\n').length
93
+ raise Error, "expected #{n} newlines"
94
+ end
94
95
  end
95
96
 
96
97
  def set_regexp
@@ -100,11 +101,8 @@ class Statement
100
101
  # Searches
101
102
 
102
103
  def detect_statement(type)
103
- statement = @context.type(type).detect do |statement|
104
- statement.match? @statement
105
- end
106
- raise Error, "does not match any '#{type}' statement" unless statement
107
- return statement
104
+ @context.type(type).detect{_1.match? @statement} ||
105
+ raise(Error, "does not match any '#{type}' statement")
108
106
  end
109
107
 
110
108
  def heap_combos_search(type)
@@ -132,16 +130,13 @@ class Statement
132
130
  def expected_instantiations(title=nil, n:nil)
133
131
  undefined = @context.symbols.undefined(self)
134
132
  if n ||= title&.match(/[1-9]\d*/)&.to_s&.to_i
135
- raise Error, "expected #{n} undefined: #{undefined.join(' ')}" unless n==undefined.length
136
- else
137
- raise Error, 'nothing was undefined' if undefined.empty?
133
+ unless n==undefined.length
134
+ raise Error, "expected #{n} undefined: #{undefined.join(' ')}"
135
+ end
136
+ elsif undefined.empty?
137
+ raise Error, 'nothing was undefined'
138
138
  end
139
- end
140
-
141
- def undefined_in_pattern
142
- @title = @title.split(':',2).first if @title
143
- undefined = @context.symbols.undefined(self)
144
- @title = "#{@title}: #{undefined.join(' ')}" unless undefined.empty?
139
+ undefined.empty? ? nil : undefined
145
140
  end
146
141
 
147
142
  # Statement type processing
@@ -149,50 +144,61 @@ class Statement
149
144
  def pattern_type(nl)
150
145
  set_regexp
151
146
  newlines_count(nl)
152
- undefined_in_pattern
147
+ undefined = (_=@context.symbols.undefined(self)).empty? ? nil : _
153
148
  follows = @context.heap.to_a[0..nl].reverse
154
- if @regexp.match? follows.map{_1.to_s}.join("\n")
155
- set_statement(support(*follows))
156
- else
157
- set_statement
158
- end
149
+ support = @regexp.match?(follows.map(&:to_s).join("\n"))?
150
+ support(*follows) : nil
151
+ set_statement(support, undefined: undefined)
159
152
  end
160
153
 
154
+ # A Tautology is an accepted true statement that immediately follows from
155
+ # an Axiom rule. It may not have any undefined terms.
161
156
  def tautology
162
157
  expected_instantiations(n:0)
163
158
  axiom = detect_statement('A')
164
159
  set_statement(support(axiom), axiom.title)
165
160
  end
166
161
 
162
+ # A Set(Assignment) is an allowed true statement that introduces at least one
163
+ # term as immediately validated by a matching Let rule.
167
164
  def set
168
165
  let = detect_statement('L')
169
- expected_instantiations(let.title)
170
- set_statement(support(let), let.title)
166
+ undefined = expected_instantiations(let.title)
167
+ set_statement(support(let), let.title, undefined: undefined)
171
168
  end
172
169
 
170
+ # A Result is a derived true statement that follows from a Map rule and
171
+ # matching true statement.
173
172
  def result
174
173
  expected_instantiations(n:0)
175
174
  mapping,s1 = heap_search('M')
176
175
  set_statement(support(mapping,s1), mapping.title)
177
176
  end
178
177
 
178
+ # An Instantiation is a derived true statement that introduces at least one
179
+ # new term as a result of an Existential rule and matching true statement.
179
180
  def instantiation
180
181
  existential,s1 = heap_search('E')
181
- expected_instantiations(existential.title)
182
- set_statement(support(existential,s1), existential.title)
182
+ undefined = expected_instantiations(existential.title)
183
+ set_statement(support(existential,s1), existential.title,
184
+ undefined: undefined)
183
185
  end
184
186
 
187
+ # A Conclusion is a derived true statement, the result of an Inference rule
188
+ # that follows from two true statements.
185
189
  def conclusion
186
190
  expected_instantiations(n:0)
187
191
  inference,s1,s2 = heap_combos_search('I')
188
192
  set_statement(support(inference,s1,s2), inference.title)
189
193
  end
190
194
 
195
+ # A Definition is an assumed true statement that defines at least one term.
191
196
  def definition
192
197
  expected_instantiations(@title)
193
198
  set_statement
194
199
  end
195
200
 
201
+ # A Postulate is an assumed true statement with all terms defined.
196
202
  def postulate
197
203
  expected_instantiations(n:0)
198
204
  set_statement
@@ -1,42 +1,36 @@
1
1
  module Korekto
2
2
  class Statements
3
3
  attr_reader :heap,:symbols,:syntax
4
+
4
5
  def initialize
5
6
  @statements = []
6
- @heap = Heap.new(13)
7
+ @heap = Heap.new Korekto.heap
7
8
  @symbols = Symbols.new
8
9
  @syntax = Syntax.new
9
10
  end
10
11
 
11
12
  def type(c) = @statements.select{_1.type==c}
12
- def length = @statements.length
13
+ def length = @statements.length
14
+ def last = @statements.last
13
15
 
14
16
  def add(statement,code,title,filename)
15
17
  c = code[0]; w = c=='W'
16
- if restatement = @statements.detect{(w or _1.type==c) and _1.to_s==statement}
17
- case restatement.type
18
- when 'D','X','S','P','T','C','R'
19
- @heap.add restatement
20
- else
18
+ if (restatement=@statements.detect{(w || _1.type==c) && _1.to_s==statement})
19
+ unless 'DXSPTCR'.include?(restatement.type)
20
+ # Only allow heap-able statements to be restated.
21
21
  raise Error, "restatement: #{restatement.code}"
22
22
  end
23
- code,_ = restatement.code
23
+ @heap.add restatement
24
+ code, = restatement.code
24
25
  title ||= restatement.title
25
26
  return code, title
26
27
  end
27
28
  statement_number = yield
28
- statement = Statement.new(statement,code,title,filename,statement_number,self)
29
+ statement=Statement.new(statement,code,title,filename,statement_number,self)
29
30
  @statements.push statement
30
- case statement.type
31
- when 'A','I','E','M','L'
32
- @symbols.define! statement
33
- when 'D','X','S'
34
- @symbols.define! statement
35
- @heap.add statement
36
- when 'P','T','C','R'
37
- @heap.add statement
38
- end
39
- return statement.code, statement.title
31
+ @symbols.define! statement if 'AIEMLDXS'.include?(statement.type)
32
+ @heap.add statement if 'DXSPTCR'.include?(statement.type)
33
+ [statement.code, statement.title]
40
34
  end
41
35
  end
42
36
  end
@@ -1,66 +1,64 @@
1
1
  module Korekto
2
2
  class Symbols
3
3
  attr_reader :t2p, :v2t
4
+
4
5
  def initialize
5
- @h = {}
6
- @t2p = {}
7
- @v2t = {}
8
- @scanner = /:\w+|./
6
+ @set = Set.new # Set of Korekto symbols(strings)
7
+ @v2t = {} # Variable to Type
8
+ @t2p = {} # Type to Pattern
9
+ @scanner = /:\w+|./ # Default scanner
9
10
  end
10
11
 
12
+ # rubocop: disable Naming/AccessorMethodName
11
13
  def set_scanner(value) = @scanner=Regexp.new(value)
14
+ # rubocop: enable Naming/AccessorMethodName
12
15
 
13
16
  def undefined(statement)
14
- undefined = []
17
+ undefined = Set.new
15
18
  if statement.pattern?
16
19
  unless statement.literal_regexp?
17
- statement.scan(@scanner){|w| undefined.push(w) unless @v2t.include?(w) or @h.include?(w)}
20
+ statement.scan(@scanner) do |w|
21
+ undefined<<w unless @v2t.include?(w) || @set.include?(w)
22
+ end
18
23
  end
19
24
  else
20
- statement.scan(@scanner){|w| undefined.push(w) unless @h.include?(w)}
25
+ statement.scan(@scanner){|w| undefined<<w unless @set.include?(w)}
21
26
  end
22
- return undefined.uniq
27
+ undefined
23
28
  end
24
29
 
25
- def define!(statement)
26
- if statement.pattern?
27
- unless statement.literal_regexp?
28
- statement.scan(@scanner){|w| @h[w]=nil unless @v2t.include?(w) or @h.include?(w)}
29
- end
30
- else
31
- statement.scan(@scanner){|w| @h[w]=nil unless @h.include?(w)}
32
- end
33
- end
30
+ def define!(statement) = undefined(statement).each{|w| @set<<w}
34
31
 
35
32
  def s2r(statement)
36
- if statement[0]=='/' and statement[-1]=='/'
37
- Regexp.new(statement[1..-2])
38
- else
39
- pattern,count,seen = '\A',0,{}
40
- statement.scan(@scanner) do |v|
41
- if n=seen[v]
42
- pattern << '\\'+n
43
- elsif type = @v2t[v]
44
- regex = @t2p[type]
45
- if type[0]=='.'
46
- pattern << regex
47
- else
48
- count += 1
49
- seen[v]=count.to_s
50
- pattern << '('+regex+')'
51
- end
33
+ return Regexp.new statement[1..-2] if statement[0]=='/' &&
34
+ statement[-1]=='/'
35
+ pattern,count,seen = '\A',0,{}
36
+ # Build pattern from statement token by token, v.
37
+ statement.scan(@scanner) do |v|
38
+ if (n=seen[v])
39
+ pattern << '\\'+n
40
+ elsif (type = @v2t[v])
41
+ regex = @t2p[type]
42
+ if type[0]=='.'
43
+ # No capture patterns start with '.'
44
+ pattern << regex
52
45
  else
53
- # Escape Regexp specials
54
- v = Regexp.quote v
55
- # To avoid collisions with back-references, isolate digit in square brackets:
56
- '0123456789'.include?(_=v[0]) and v[0]='['+_+']'
57
- pattern << v
46
+ count += 1
47
+ seen[v]=count.to_s
48
+ pattern << '('+regex+')'
58
49
  end
50
+ else
51
+ # Escape Regexp specials
52
+ v = Regexp.quote v
53
+ # To avoid collisions with back-references,
54
+ # isolate digit in square brackets:
55
+ '0123456789'.include?(_=v[0]) and v[0]='['+_+']'
56
+ pattern << v
59
57
  end
60
- raise Error, 'pattern with no captures' if count < 1
61
- pattern << '\Z'
62
- Regexp.new(pattern)
63
58
  end
59
+ raise Error, 'pattern with no captures' if count < 1
60
+ pattern << '\Z'
61
+ Regexp.new(pattern)
64
62
  end
65
63
  end
66
64
  end
@@ -1,14 +1,17 @@
1
1
  module Korekto
2
2
  class Syntax
3
3
  def initialize = @a=[]
4
- def each = @a.each{|s| yield s}
4
+ def each(&blk) = @a.each{|s| blk[s]}
5
5
 
6
6
  def push(s)
7
7
  # ensure it'll eval on string and returns boolean
8
8
  b = ''.instance_eval(s)
9
+ # rubocop: disable Style/DoubleNegation
9
10
  raise Error, 'syntax rule must eval boolean' unless b==!!b
11
+ # rubocop: enable Style/DoubleNegation
10
12
  @a.push(s)
11
- rescue
13
+ rescue StandardError
14
+ raise if $!.is_a? Error
12
15
  raise Error, "#{$!.class}: #{s}"
13
16
  end
14
17
  end
data/lib/korekto.rb CHANGED
@@ -1,14 +1,51 @@
1
1
  module Korekto
2
- VERSION = '1.6.210409'
3
- class Error < Exception; end
4
- require 'korekto/symbols'
5
- require 'korekto/syntax'
6
- require 'korekto/heap'
7
- require 'korekto/statement'
8
- require 'korekto/statements'
9
- require 'korekto/main'
10
- def Korekto.run = Korekto::Main.new.run
2
+ class Error < RuntimeError; end
3
+
4
+ VERSION = '2.0.231231'
5
+
6
+ def self.trace=(value)
7
+ @@trace = value
8
+ end
9
+ def self.trace?
10
+ @@trace
11
+ end
12
+ Korekto.trace = false
13
+
14
+ def self.patch=(value)
15
+ @@patch = value
16
+ end
17
+ def self.patch?
18
+ @@patch
19
+ end
20
+ Korekto.patch = false
21
+
22
+ def self.heap=(value)
23
+ @@heap = value
24
+ end
25
+ def self.heap
26
+ @@heap
27
+ end
28
+ Korekto.heap = 13
29
+
30
+ def self.scrape?
31
+ @@scrape
32
+ end
33
+ def self.scrape=(value)
34
+ @@scrape = value
35
+ end
36
+ Korekto.scrape = false
37
+
38
+ def self.run
39
+ require 'korekto/symbols'
40
+ require 'korekto/syntax'
41
+ require 'korekto/heap'
42
+ require 'korekto/statement'
43
+ require 'korekto/statements'
44
+ require 'korekto/main'
45
+ Korekto::Main.new.run
46
+ end
11
47
  end
48
+
12
49
  # Requires:
13
50
  # `ruby`
14
51
  # `nvim`
@@ -1,30 +1,38 @@
1
- Neovim.plugin do |plug|
2
- plug.command(:Korekto) do |nvim|
3
- validations = nvim.command_output('w !korekto --edits').strip.split("\n").map(&:strip)
4
- msg = 'OK'
1
+ module KorektoPlug
2
+ KOREKTO = `which korekto`.strip
3
+ VERSION = `#{KOREKTO} --version`.strip
4
+
5
+ def self.korekto(nvim, options='')
6
+ validations = nvim.command_output("w !#{KOREKTO} #{options}")
7
+ .strip.split("\n").map(&:strip)
8
+ msg = "Korekto! #{VERSION}"
5
9
  unless validations.empty?
6
10
  buf = nvim.get_current_buf
7
- while validation = validations.shift
11
+ while (validation = validations.shift)
8
12
  fields = validation.split(':',4)
9
13
  n = fields[1].to_i
10
- if n > 0
11
- fn,ln,code,title = fields
12
- if code=='?' or code=='!'
13
- # move onto error if on page
14
- nvim.get_current_win.set_cursor([n,0]) if fn=='-'
15
- # echo error message
16
- msg = "#{fn}:#{ln}: #{title}"
17
- # and stop.
18
- break
19
- elsif fn=='-'
20
- line = buf[n].sub(/#.*$/,'').rstrip
21
- line << "\t##{code}"
22
- line << " #{title}" unless title==''
23
- buf[n] = line unless line == buf[n]
24
- end
14
+ next unless n.positive?
15
+ fn,ln,code,title = fields
16
+ if %w[? !].include?(code)
17
+ # move onto error if on page
18
+ nvim.get_current_win.set_cursor([n,0]) if fn=='-'
19
+ # echo error message
20
+ msg = "#{fn}:#{ln}: #{title}"
21
+ # and stop.
22
+ break
23
+ elsif fn=='-'
24
+ line = buf[n].sub(/#.*$/,'').rstrip
25
+ line << "\t##{code}"
26
+ line << " #{title}" unless title==''
27
+ buf[n] = line unless line == buf[n]
25
28
  end
26
29
  end
27
30
  end
28
31
  nvim.command "echom #{msg.inspect}"
29
32
  end
33
+
34
+ Neovim.plugin do |plug|
35
+ plug.command(:Korekto){|nvim| KorektoPlug.korekto(nvim)}
36
+ plug.command(:KorektoPatch){|nvim| KorektoPlug.korekto(nvim, '--patch')}
37
+ end
30
38
  end
@@ -1,21 +1,31 @@
1
- syntax match Unsup /#[A-Z]\d\+\(\.\w\+\)\?/ contained containedin=Type
2
- syntax match Sup /#[A-Z]\d\+\(\.\w\+\)\?\/\S\+/ contained containedin=Type
3
- syntax match Title / [^:]\+/ contained containedin=Type
4
- syntax match Undef /:[^#]\+$/ contained containedin=Type
5
- syntax match Type /\s#[A-Z][^#]\+/ contains=Unsup,Sup,Title,Undef
6
- syntax match Comment /^\s*#.\+$/
7
- syntax match Syntax /^[?] \S.\+$/
8
- syntax match Patch /^::[A-Z]\w\+#\w\+[^=]\+=.\+$/
9
- syntax match Import /^< [\/A-Za-z\_\-\.]\+$/
10
- syntax match Setting /^! \S.*$/
11
- highlight Unsup ctermfg=brown
12
- highlight Sup ctermfg=darkgreen
13
- highlight Title ctermfg=darkblue
14
- highlight Undef ctermfg=red
15
- highlight Comment ctermfg=darkblue
16
- highlight Import ctermfg=brown
17
- highlight Patch ctermfg=darkgrey
18
- highlight Syntax ctermfg=darkgrey
19
- highlight Setting ctermfg=darkmagenta
20
- setlocal tabstop=23
1
+ "### Korekto Syntax ###
2
+ " Korekto Type
3
+ syntax match KorektoUnsup /#[A-Z]\d\+\(\.\w\+\)\?\(\/\)\@!/ containedin=KorektoType
4
+ syntax match KorektoSup /#[A-Z]\d\+\(\.\w\+\)\?\/\S\+/ contained containedin=KorektoType
5
+ syntax match KorektoTitle /\(#[A-Z]\d\S*\s\)\@<=[^:]\+/ contained containedin=KorektoType
6
+ syntax match KorektoUndef /\(:\)\@<=.\+$/ contained containedin=KorektoType
7
+ syntax match KorektoType /#[A-Z]\d[^#]\+$/ contains=KorektoUnsup,KorektoSup,KorektoTitle,KorektoUndef
8
+ " Korekto Statement
9
+ syntax match KorektoLetter /[A-Za-z𝐀-𝟿]/ contained containedin=KorektoStatement
10
+ syntax match KorektoNumber /[0-9]/ contained containedin=KorektoStatement
11
+ syntax match KorektoPunctuation /[^0-9A-Za-z𝐀-𝟿]/ contained containedin=KorektoStatement
12
+ syntax match KorektoStatement /^.\+\(\s#[A-Z]\d\)\@=/ contains=KorektoLetter,KorektoNumber,KorektoPunctuation
13
+ " Korekto Non-Statement
14
+ syntax match KorektoComment /^#\+\s.\+/
15
+ syntax match KorektoPatch /^::[A-Z].\+/
16
+ syntax match KorektoCommand /^[<!?]\s.\+/
17
+ "### Korekto Highlighting ###
18
+ highlight KorektoUnsup ctermfg=brown
19
+ highlight KorektoSup ctermfg=darkgreen
20
+ highlight KorektoTitle ctermfg=blue
21
+ highlight KorektoUndef ctermfg=red
22
+ highlight KorektoComment ctermfg=darkblue
23
+ highlight KorektoPatch ctermfg=grey
24
+ highlight KorektoCommand ctermfg=lightgrey
25
+ highlight KorektoPunctuation ctermfg=darkgrey
26
+ highlight KorektoNumber ctermfg=darkred
27
+ highlight KorektoLetter ctermfg=black
28
+ "### Settings ###
29
+ setlocal tabstop=3
30
+ map <leader><F7> :KorektoPatch<CR>
21
31
  map <F7> :Korekto<CR>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: korekto
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.210409
4
+ version: 2.0.231231
5
5
  platform: ruby
6
6
  authors:
7
- - carlosjhr64
8
- autorequire:
7
+ - CarlosJHR64
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-09 00:00:00.000000000 Z
11
+ date: 2023-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: help_parser
@@ -16,30 +16,131 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '7.0'
19
+ version: '8.2'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 7.0.200907
22
+ version: 8.2.230210
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: '7.0'
29
+ version: '8.2'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 7.0.200907
32
+ version: 8.2.230210
33
+ - !ruby/object:Gem::Dependency
34
+ name: colorize
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.1'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.1.0
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.1'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.1.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: cucumber
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '9.1'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 9.1.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '9.1'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 9.1.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: parser
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '3.2'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 3.2.2
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.2'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 3.2.2
93
+ - !ruby/object:Gem::Dependency
94
+ name: rubocop
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '1.59'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 1.59.0
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.59'
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 1.59.0
113
+ - !ruby/object:Gem::Dependency
114
+ name: test-unit
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '3.6'
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 3.6.1
123
+ type: :development
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '3.6'
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 3.6.1
33
133
  description: |
34
134
  A general proof checker.
35
135
 
36
- Works with neovim(nvim).
136
+ Works with [neovim](https://github.com/neovim/neovim).
37
137
  email: carlosjhr64@gmail.com
38
138
  executables:
39
139
  - korekto
40
140
  extensions: []
41
141
  extra_rdoc_files: []
42
142
  files:
143
+ - README.md
43
144
  - bin/korekto
44
145
  - lib/korekto.rb
45
146
  - lib/korekto/heap.rb
@@ -55,7 +156,7 @@ homepage: https://github.com/carlosjhr64/korekto
55
156
  licenses:
56
157
  - MIT
57
158
  metadata: {}
58
- post_install_message:
159
+ post_install_message:
59
160
  rdoc_options: []
60
161
  require_paths:
61
162
  - lib
@@ -70,12 +171,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
171
  - !ruby/object:Gem::Version
71
172
  version: '0'
72
173
  requirements:
73
- - 'ruby: ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-linux]'
74
- - 'nvim: NVIM v0.4.4'
75
- - 'neovim-ruby-host: 0.8.1'
76
- - 'xdg-open: xdg-open 1.1.3+'
77
- rubygems_version: 3.2.15
78
- signing_key:
174
+ - 'git: 2.30'
175
+ - 'neovim-ruby-host: 0.9'
176
+ - 'nvim: 0.9'
177
+ - 'ruby: 3.3'
178
+ - 'xdg-open: 1.1'
179
+ rubygems_version: 3.5.3
180
+ signing_key:
79
181
  specification_version: 4
80
182
  summary: A general proof checker.
81
183
  test_files: []