ellipses 0.0.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 +7 -0
- data/BEN/304/260OKU.md +137 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE.md +675 -0
- data/README.md +1 -0
- data/bin/src +12 -0
- data/bin/srv +12 -0
- data/ellipses.gemspec +39 -0
- data/lib/ellipses-client.rb +4 -0
- data/lib/ellipses-server.rb +4 -0
- data/lib/ellipses.rb +5 -0
- data/lib/ellipses/client.rb +17 -0
- data/lib/ellipses/client/application.rb +77 -0
- data/lib/ellipses/client/cli.rb +24 -0
- data/lib/ellipses/client/cli/compile.rb +21 -0
- data/lib/ellipses/client/cli/decompile.rb +21 -0
- data/lib/ellipses/client/cli/init.rb +23 -0
- data/lib/ellipses/client/cli/update.rb +19 -0
- data/lib/ellipses/client/cli/validate.rb +21 -0
- data/lib/ellipses/client/cli/version.rb +17 -0
- data/lib/ellipses/client/command.rb +69 -0
- data/lib/ellipses/client/commands.rb +23 -0
- data/lib/ellipses/client/commands/include.rb +22 -0
- data/lib/ellipses/client/commands/reject.rb +17 -0
- data/lib/ellipses/client/commands/select.rb +17 -0
- data/lib/ellipses/client/commands/substitute.rb +23 -0
- data/lib/ellipses/client/commands/translate.rb +15 -0
- data/lib/ellipses/client/config.rb +33 -0
- data/lib/ellipses/client/directive.rb +39 -0
- data/lib/ellipses/client/error.rb +10 -0
- data/lib/ellipses/client/lines.rb +154 -0
- data/lib/ellipses/client/meta.rb +79 -0
- data/lib/ellipses/client/meta_file.rb +56 -0
- data/lib/ellipses/client/parser.rb +74 -0
- data/lib/ellipses/client/repository.rb +101 -0
- data/lib/ellipses/client/source.rb +69 -0
- data/lib/ellipses/error.rb +5 -0
- data/lib/ellipses/server.rb +10 -0
- data/lib/ellipses/server/application.rb +85 -0
- data/lib/ellipses/server/cli.rb +21 -0
- data/lib/ellipses/server/cli/dump.rb +20 -0
- data/lib/ellipses/server/cli/validate.rb +21 -0
- data/lib/ellipses/server/cli/version.rb +17 -0
- data/lib/ellipses/server/error.rb +7 -0
- data/lib/ellipses/server/meta.rb +69 -0
- data/lib/ellipses/server/meta_file.rb +39 -0
- data/lib/ellipses/server/repository.rb +47 -0
- data/lib/ellipses/server/symbols.rb +146 -0
- data/lib/ellipses/support.rb +3 -0
- data/lib/ellipses/support/deflate_path.rb +14 -0
- data/lib/ellipses/support/digest.rb +13 -0
- data/lib/ellipses/support/entropy.rb +17 -0
- data/lib/ellipses/support/expand_path.rb +13 -0
- data/lib/ellipses/support/intersperse_arrays.rb +14 -0
- data/lib/ellipses/support/prefixize_non_blank.rb +16 -0
- data/lib/ellipses/support/refinements.rb +20 -0
- data/lib/ellipses/support/sanitize_path.rb +101 -0
- data/lib/ellipses/support/search_path.rb +19 -0
- data/lib/ellipses/support/shell.rb +85 -0
- data/lib/ellipses/support/to_range.rb +15 -0
- data/lib/ellipses/support/ui.rb +55 -0
- data/lib/ellipses/support/updatelines.rb +19 -0
- data/lib/ellipses/support/writelines.rb +11 -0
- data/lib/ellipses/version.rb +5 -0
- metadata +264 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'shellwords'
|
4
|
+
|
5
|
+
module Ellipses
|
6
|
+
module Client
|
7
|
+
class Parser
|
8
|
+
PATTERN = /
|
9
|
+
^
|
10
|
+
(?<prefix>\s*)
|
11
|
+
(?<directive>(?:[.]|>){3,}.*)
|
12
|
+
/x
|
13
|
+
|
14
|
+
Parsed = Struct.new :lexemes, :prefix, keyword_init: true do
|
15
|
+
def to_a
|
16
|
+
lexemes
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Lexeme = Struct.new :token, :argv, keyword_init: true do
|
21
|
+
def self.from_strings(strings)
|
22
|
+
new token: strings.shift.downcase, argv: strings
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
token
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def parse(line)
|
32
|
+
return unless (match = line.match(PATTERN))
|
33
|
+
|
34
|
+
parser = new(match[:directive])
|
35
|
+
|
36
|
+
Parsed.new lexemes: parser.(), prefix: match[:prefix]
|
37
|
+
end
|
38
|
+
|
39
|
+
def match?(line)
|
40
|
+
!line.match(PATTERN).nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
private_class_method :new
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :line
|
47
|
+
|
48
|
+
def initialize(line)
|
49
|
+
@line = line
|
50
|
+
end
|
51
|
+
|
52
|
+
def call
|
53
|
+
Shellwords.split(preprocess(line)).slice_after('|').map do |group|
|
54
|
+
group.pop if group.last == '|'
|
55
|
+
Lexeme.from_strings(group)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
SUBSTITUTIONS = {
|
62
|
+
/^[.]{3,}/ => 'include',
|
63
|
+
/^>{3,}/ => ''
|
64
|
+
}.freeze
|
65
|
+
|
66
|
+
def preprocess(rawline)
|
67
|
+
rawline.strip.tap do |line|
|
68
|
+
SUBSTITUTIONS.each { |pattern, replace| line.gsub!(pattern, replace) }
|
69
|
+
line.strip!
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ellipses
|
4
|
+
module Client
|
5
|
+
class Repository
|
6
|
+
File = Struct.new :path, :source, :digest, :registered, keyword_init: true do
|
7
|
+
def save(all: true)
|
8
|
+
return if !registered && !all
|
9
|
+
return if digest == (new_digest = Support.digest(*source.lines))
|
10
|
+
|
11
|
+
Support.writelines(path, source.lines)
|
12
|
+
Support::UI.notice(path)
|
13
|
+
|
14
|
+
self.digest = new_digest
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private_constant :File
|
19
|
+
|
20
|
+
attr_reader :loader
|
21
|
+
|
22
|
+
def initialize(loader)
|
23
|
+
@loader = loader
|
24
|
+
@files = {}
|
25
|
+
@memo = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](path)
|
29
|
+
@files[memo(path)].source
|
30
|
+
end
|
31
|
+
|
32
|
+
def each_source
|
33
|
+
each { |_, file| yield(file.source) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def load(loader)
|
37
|
+
Dir.chdir loader.directory do
|
38
|
+
loader.read.each { |meta| register_internal(meta.source, Source.from_file(meta.source, meta.series)) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def save(all: true)
|
43
|
+
n = 0
|
44
|
+
each { |_, file| file.save(all: all) and (n += 1) }
|
45
|
+
n.positive?
|
46
|
+
end
|
47
|
+
|
48
|
+
def dump
|
49
|
+
meta = Meta.new []
|
50
|
+
|
51
|
+
each do |path, file|
|
52
|
+
next unless file.registered
|
53
|
+
next unless (source = file.source).series
|
54
|
+
|
55
|
+
meta << Meta::Source.new(source: path, series: source.series)
|
56
|
+
end
|
57
|
+
|
58
|
+
meta
|
59
|
+
end
|
60
|
+
|
61
|
+
def register(path, *args, **kwargs)
|
62
|
+
if @files.key?(key = memo(path))
|
63
|
+
return @files[key].tap { |file| file.registered = true }.source
|
64
|
+
end
|
65
|
+
|
66
|
+
register_internal(key, Source.from_file(path, *args, **kwargs))
|
67
|
+
end
|
68
|
+
|
69
|
+
def unregister(path)
|
70
|
+
return unless @files.key?(key = memo(path))
|
71
|
+
|
72
|
+
@files[key].tap { |file| file.registered = false }.source
|
73
|
+
end
|
74
|
+
|
75
|
+
def registered?(path)
|
76
|
+
return false unless @files.key?(key = memo(path))
|
77
|
+
|
78
|
+
@files[key].registered
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def memo(path)
|
84
|
+
@memo[path] ||= Support.deflate_path(path, loader.directory)
|
85
|
+
end
|
86
|
+
|
87
|
+
def each(&block)
|
88
|
+
Dir.chdir(loader.directory) { @files.each(&block) }
|
89
|
+
end
|
90
|
+
|
91
|
+
def register_internal(key, source)
|
92
|
+
@files[key] = File.new path: key,
|
93
|
+
source: source,
|
94
|
+
digest: Support.digest(*source.lines),
|
95
|
+
registered: true
|
96
|
+
|
97
|
+
source
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ellipses/server'
|
4
|
+
|
5
|
+
module Ellipses
|
6
|
+
module Client
|
7
|
+
class Source
|
8
|
+
attr_reader :series
|
9
|
+
|
10
|
+
def initialize(lines, series)
|
11
|
+
@lines = lines
|
12
|
+
@series = series || []
|
13
|
+
end
|
14
|
+
|
15
|
+
def lines
|
16
|
+
@lines.is_a?(Lines) ? @lines : (@lines = Lines.new @lines.to_a)
|
17
|
+
end
|
18
|
+
|
19
|
+
def decompile
|
20
|
+
return if series.empty?
|
21
|
+
|
22
|
+
while (lock = series.shift)
|
23
|
+
lines.substitute_uniq_like_chunk!(lock.insertion, lock.directive)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def compile(server)
|
28
|
+
new_lines = Lines[]
|
29
|
+
|
30
|
+
lines.each do |line|
|
31
|
+
new_lines.append(*(Parser.match?(line) ? execute_and_record(line, server) : line))
|
32
|
+
end
|
33
|
+
|
34
|
+
@lines = new_lines
|
35
|
+
end
|
36
|
+
|
37
|
+
def recompile(server)
|
38
|
+
decompile
|
39
|
+
compile(server)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def execute(line, server, **kwargs)
|
45
|
+
raise Error, "No directive found: #{line}" unless (parsed = Parser.parse(line))
|
46
|
+
|
47
|
+
directive = Directive.new(parsed.lexemes, server)
|
48
|
+
|
49
|
+
outlines = directive.execute(**kwargs)
|
50
|
+
outlines.map! { |outline| Support.prefixize_non_blank(outline, parsed.prefix, excludes: "\f\f\f") }
|
51
|
+
|
52
|
+
Lines[outlines]
|
53
|
+
end
|
54
|
+
|
55
|
+
def execute_and_record(line, server, **kwargs)
|
56
|
+
result = execute(line, server, **kwargs)
|
57
|
+
series << Meta::Lock.new(directive: line, insertion: result.insertion_by_entropy)
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
class << self
|
62
|
+
def from_file(file, series = nil)
|
63
|
+
lines = File.readlines(Support.file!(file, error: Error)).lazy
|
64
|
+
new lines, series
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ellipses'
|
4
|
+
|
5
|
+
require_relative 'server/error'
|
6
|
+
require_relative 'server/meta'
|
7
|
+
require_relative 'server/meta_file'
|
8
|
+
require_relative 'server/symbols'
|
9
|
+
require_relative 'server/repository'
|
10
|
+
require_relative 'server/application'
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ellipses
|
4
|
+
module Server
|
5
|
+
class Application
|
6
|
+
DEFAULT_PORT = 0
|
7
|
+
|
8
|
+
Instance = Struct.new :uri, :repository, :port, keyword_init: true do
|
9
|
+
def to_s
|
10
|
+
self.class.ident(uri, port)
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def ident(uri, port)
|
15
|
+
"#{uri}:#{port || DEFAULT_PORT}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :paths, :intersperse
|
21
|
+
|
22
|
+
def initialize(paths, intersperse: "\n")
|
23
|
+
@paths = setup_paths(paths)
|
24
|
+
@intersperse = intersperse
|
25
|
+
@instances = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def out(uri:, symbols:, port: nil)
|
29
|
+
[].tap { |chunks| symbols.each { |symbol| chunks.append(*instance(uri, port).repository[symbol]) } }
|
30
|
+
end
|
31
|
+
|
32
|
+
def dump(uri:, symbols:, port: nil)
|
33
|
+
Support.intersperse_arrays(out(uri: uri, symbols: symbols, port: port), intersperse).flatten
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate(uri)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def available?(uri)
|
41
|
+
!scan(uri).nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
def available!(uri)
|
45
|
+
return if available?(uri)
|
46
|
+
|
47
|
+
raise Error, "Repository not found in path: #{uri}"
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def instance(uri, port = nil)
|
53
|
+
ident = Instance.ident(uri, port)
|
54
|
+
return @instances[ident] if @instances.key? ident
|
55
|
+
|
56
|
+
directory = scan(uri)
|
57
|
+
raise Error, "No repository instance found: #{uri}" unless directory
|
58
|
+
|
59
|
+
instance = Instance.new uri: uri, repository: Repository.load(directory), port: port
|
60
|
+
@instances[ident] = instance
|
61
|
+
end
|
62
|
+
|
63
|
+
def setup_paths(paths)
|
64
|
+
paths = paths.filter_map { |path| Support.dir(path) }.uniq
|
65
|
+
raise Error, 'No valid path found' if paths.empty?
|
66
|
+
|
67
|
+
paths
|
68
|
+
end
|
69
|
+
|
70
|
+
def scan(uri)
|
71
|
+
paths.map { |path| ::File.join(path, uri) }.find { |path| MetaFile.valid?(path) }
|
72
|
+
end
|
73
|
+
|
74
|
+
class << self
|
75
|
+
def dump(path, *symbols, **kwargs)
|
76
|
+
new([Support.dir!(path, error: Error)], **kwargs).dump(uri: '.', symbols: symbols)
|
77
|
+
end
|
78
|
+
|
79
|
+
def validate(path, **kwargs)
|
80
|
+
new([Support.dir!(path, error: Error)], **kwargs).validate('.')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/cli'
|
4
|
+
|
5
|
+
require 'ellipses/server'
|
6
|
+
|
7
|
+
module Ellipses
|
8
|
+
module Server
|
9
|
+
module CLI
|
10
|
+
module Commands
|
11
|
+
extend Dry::CLI::Registry
|
12
|
+
|
13
|
+
Dir[File.join(__dir__, 'cli/*.rb')].each { |command| require command }
|
14
|
+
|
15
|
+
register 'version', Version, aliases: ['v', '-v', '--version']
|
16
|
+
register 'dump', Dump
|
17
|
+
register 'validate', Validate
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ellipses
|
4
|
+
module Server
|
5
|
+
module CLI
|
6
|
+
module Commands
|
7
|
+
class Dump < Dry::CLI::Command
|
8
|
+
desc 'Dump symbols'
|
9
|
+
|
10
|
+
argument :path, type: :string, required: true, desc: 'Repository path'
|
11
|
+
argument :symbols, type: :array, required: false, desc: 'Symbols'
|
12
|
+
|
13
|
+
def call(path:, symbols: [], **)
|
14
|
+
puts Ellipses::Server::Application.dump path, *symbols
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/cli'
|
4
|
+
|
5
|
+
module Ellipses
|
6
|
+
module Server
|
7
|
+
module CLI
|
8
|
+
module Commands
|
9
|
+
class Validate < Dry::CLI::Command
|
10
|
+
desc 'Validate repository'
|
11
|
+
|
12
|
+
argument :path, type: :string, required: true, desc: 'Repository path'
|
13
|
+
|
14
|
+
def call(path:)
|
15
|
+
Ellipses::Server::Application.validate path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|