ellipses 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/BEN/304/260OKU.md +137 -0
  3. data/CHANGELOG.md +9 -0
  4. data/LICENSE.md +675 -0
  5. data/README.md +1 -0
  6. data/bin/src +12 -0
  7. data/bin/srv +12 -0
  8. data/ellipses.gemspec +39 -0
  9. data/lib/ellipses-client.rb +4 -0
  10. data/lib/ellipses-server.rb +4 -0
  11. data/lib/ellipses.rb +5 -0
  12. data/lib/ellipses/client.rb +17 -0
  13. data/lib/ellipses/client/application.rb +77 -0
  14. data/lib/ellipses/client/cli.rb +24 -0
  15. data/lib/ellipses/client/cli/compile.rb +21 -0
  16. data/lib/ellipses/client/cli/decompile.rb +21 -0
  17. data/lib/ellipses/client/cli/init.rb +23 -0
  18. data/lib/ellipses/client/cli/update.rb +19 -0
  19. data/lib/ellipses/client/cli/validate.rb +21 -0
  20. data/lib/ellipses/client/cli/version.rb +17 -0
  21. data/lib/ellipses/client/command.rb +69 -0
  22. data/lib/ellipses/client/commands.rb +23 -0
  23. data/lib/ellipses/client/commands/include.rb +22 -0
  24. data/lib/ellipses/client/commands/reject.rb +17 -0
  25. data/lib/ellipses/client/commands/select.rb +17 -0
  26. data/lib/ellipses/client/commands/substitute.rb +23 -0
  27. data/lib/ellipses/client/commands/translate.rb +15 -0
  28. data/lib/ellipses/client/config.rb +33 -0
  29. data/lib/ellipses/client/directive.rb +39 -0
  30. data/lib/ellipses/client/error.rb +10 -0
  31. data/lib/ellipses/client/lines.rb +154 -0
  32. data/lib/ellipses/client/meta.rb +79 -0
  33. data/lib/ellipses/client/meta_file.rb +56 -0
  34. data/lib/ellipses/client/parser.rb +74 -0
  35. data/lib/ellipses/client/repository.rb +101 -0
  36. data/lib/ellipses/client/source.rb +69 -0
  37. data/lib/ellipses/error.rb +5 -0
  38. data/lib/ellipses/server.rb +10 -0
  39. data/lib/ellipses/server/application.rb +85 -0
  40. data/lib/ellipses/server/cli.rb +21 -0
  41. data/lib/ellipses/server/cli/dump.rb +20 -0
  42. data/lib/ellipses/server/cli/validate.rb +21 -0
  43. data/lib/ellipses/server/cli/version.rb +17 -0
  44. data/lib/ellipses/server/error.rb +7 -0
  45. data/lib/ellipses/server/meta.rb +69 -0
  46. data/lib/ellipses/server/meta_file.rb +39 -0
  47. data/lib/ellipses/server/repository.rb +47 -0
  48. data/lib/ellipses/server/symbols.rb +146 -0
  49. data/lib/ellipses/support.rb +3 -0
  50. data/lib/ellipses/support/deflate_path.rb +14 -0
  51. data/lib/ellipses/support/digest.rb +13 -0
  52. data/lib/ellipses/support/entropy.rb +17 -0
  53. data/lib/ellipses/support/expand_path.rb +13 -0
  54. data/lib/ellipses/support/intersperse_arrays.rb +14 -0
  55. data/lib/ellipses/support/prefixize_non_blank.rb +16 -0
  56. data/lib/ellipses/support/refinements.rb +20 -0
  57. data/lib/ellipses/support/sanitize_path.rb +101 -0
  58. data/lib/ellipses/support/search_path.rb +19 -0
  59. data/lib/ellipses/support/shell.rb +85 -0
  60. data/lib/ellipses/support/to_range.rb +15 -0
  61. data/lib/ellipses/support/ui.rb +55 -0
  62. data/lib/ellipses/support/updatelines.rb +19 -0
  63. data/lib/ellipses/support/writelines.rb +11 -0
  64. data/lib/ellipses/version.rb +5 -0
  65. 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ellipses
4
+ Error = Class.new StandardError
5
+ 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
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ellipses
4
+ module Server
5
+ module CLI
6
+ module Commands
7
+ class Version < Dry::CLI::Command
8
+ desc 'Print version'
9
+
10
+ def call(*)
11
+ puts '1.0.0'
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ellipses
4
+ module Server
5
+ Error = Class.new Ellipses::Error
6
+ end
7
+ end