nestedtext 0.1.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.
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nestedtext/errors"
4
+
5
+ module NestedText
6
+ class LineScanner
7
+ def initialize(io)
8
+ @io = io
9
+ @next_line = nil
10
+ prepare_next_line
11
+ end
12
+
13
+ def empty?
14
+ @next_line.nil?
15
+ end
16
+
17
+ def read_next
18
+ raise Errors::LineScannerIsEmpty if empty?
19
+
20
+ line = @next_line
21
+ prepare_next_line
22
+ line.prev.prev = nil unless line.prev.nil? # GC: break the chain
23
+ line
24
+ end
25
+
26
+ def peek
27
+ @next_line
28
+ end
29
+
30
+ private
31
+
32
+ def prepare_next_line
33
+ line = nil
34
+ loop do
35
+ linestr = @io.gets&.chomp
36
+ lineno = @io.lineno - 1 # Be 0-based
37
+ line = linestr.nil? ? nil : Line.new(linestr, lineno, @next_line)
38
+ break if line.nil? || !%i[blank comment].include?(line.tag)
39
+ end
40
+ @next_line = line
41
+ end
42
+ end
43
+
44
+ class InlineScanner
45
+ attr_reader :line, :pos
46
+
47
+ def initialize(line)
48
+ @line = line
49
+ @pos = 0
50
+ end
51
+
52
+ def empty?
53
+ @pos >= @line.content.length
54
+ end
55
+
56
+ def remaining
57
+ @line.content[@pos..]
58
+ end
59
+
60
+ def read_next
61
+ raise Errors::InlineScannerIsEmpty if empty?
62
+
63
+ @pos += 1
64
+ @line.content[@pos - 1]
65
+ end
66
+
67
+ def peek
68
+ empty? ? nil : @line.content[@pos]
69
+ end
70
+ end
71
+
72
+ class Line
73
+ # Reference: https://nestedtext.org/en/latest/file_format.html
74
+ ALLOWED_LINE_TAGS = [
75
+ :comment, # a comment
76
+ :blank,
77
+ :list_item, # - item
78
+ :dict_item, # key: value (or value on next line)
79
+ :string_item, # > a string, can continue next line
80
+ :key_item, # : key on a line
81
+ :inline_dict, # {key1: value1, key2: value2}
82
+ :inline_list, # [value1, value2]
83
+ :unrecognized # could not be determined
84
+ ]
85
+
86
+ attr_accessor :prev
87
+ attr_reader :tag, :content, :indentation, :attribs, :lineno
88
+
89
+ def initialize(content, lineno, prev_line)
90
+ @content = content
91
+ @lineno = lineno
92
+ @prev = prev_line
93
+ @attribs = Hash.new(nil)
94
+ @tag = nil
95
+ @indentation = 0
96
+ detect_line_tag_and_indentation
97
+ end
98
+
99
+ # def length
100
+ # @content.length
101
+ # end
102
+
103
+ # def [](index)
104
+ # @content[index]
105
+ # end
106
+
107
+ def tag=(tag)
108
+ @tag = tag
109
+ raise Errors::LineTagUnknown.new(self, tag) unless ALLOWED_LINE_TAGS.include?(@tag)
110
+ end
111
+
112
+ def to_s
113
+ "[##{@lineno}] #{" " * @indentation}#{@content}"
114
+ end
115
+
116
+ private
117
+
118
+ # TODO: this regex must unit tested.
119
+ PATTERN_DICT_ITEM = /^
120
+ (?<key>[^\s].*?) # Key must start with a non-whitespace character, and goes until first
121
+ \s*: # first optional space, or :-separator
122
+ (?: # Value part is optional
123
+ \p{Space} # Must have a space after :-separator
124
+ (?<value>.*) # Value is everything to the end of the line
125
+ )?
126
+ $/x
127
+
128
+ def detect_line_tag_and_indentation
129
+ @indentation += 1 while @indentation < @content.length && @content[@indentation] == " "
130
+ @content = @content[@indentation..]
131
+
132
+ if @content.length == 0
133
+ self.tag = :blank
134
+ elsif @content[0] == "#"
135
+ self.tag = :comment
136
+ elsif @content =~ /^:(?: |$)/
137
+ self.tag = :key_item
138
+ @attribs["key"] = @content[2..] || ""
139
+ elsif @content =~ /^-(?: |$)/
140
+ self.tag = :list_item
141
+ @attribs["value"] = @content[2..]
142
+ elsif @content =~ /^>(?: |$)/
143
+ self.tag = :string_item
144
+ @attribs["value"] = @content[2..] || ""
145
+ elsif @content[0] == "{"
146
+ self.tag = :inline_dict
147
+ elsif @content[0] == "["
148
+ # TODO: merge path of inline dict and list and just set :inline?
149
+ self.tag = :inline_list
150
+ elsif @content =~ PATTERN_DICT_ITEM
151
+ self.tag = :dict_item
152
+ @attribs["key"] = Regexp.last_match(:key)
153
+ @attribs["value"] = Regexp.last_match(:value)
154
+ else
155
+ # Don't raise error here, as this line might not have been consumed yet,
156
+ # thus could hide an error that we detect when parsing the previous line.
157
+ self.tag = :unrecognized
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NestedText
4
+ VERSION = "0.1.0"
5
+ end
data/lib/nestedtext.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "nestedtext/encode"
4
+ require_relative "nestedtext/decode"
5
+ require_relative "nestedtext/core_ext"
6
+
7
+ module NestedText
8
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/nestedtext/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "nestedtext"
7
+ spec.version = NestedText::VERSION
8
+ spec.authors = ["Erik Westrup"]
9
+ spec.email = ["erik.westrup@gmail.com"]
10
+
11
+ spec.summary = "A ruby library for the human friendly data format NestedText https://nestedtext.org/"
12
+ spec.description = "A ruby implementation the NestedText data format. There is support for decoding a NestedText file or string to Ruby data structures, as well as encoding Ruby objects to a NestedText file or string. Furthermore there is support for serialization and deserialization of custom classes. Support for v3.2.1 of the data format will all official tests passing."
13
+ spec.homepage = "https://github.com/erikw/nestedtext-ruby/"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = [">= 3.0", "< 4"]
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/erikw/nestedtext-ruby/"
19
+ spec.metadata["changelog_uri"] = "https://github.com/erikw/nestedtext-ruby/blob/main/CHANGELOG.md"
20
+
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject do |f|
23
+ f.match(%r{\A(?:test/|script/|\.github/|\.gitmodules|Rakefile|TODO\.txt|\.codeclimate\.yml|\.vimlocal|\.simplecov)})
24
+ end
25
+ end
26
+ spec.require_paths = ["lib"]
27
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nestedtext
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Erik Westrup
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-01-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A ruby implementation the NestedText data format. There is support for
14
+ decoding a NestedText file or string to Ruby data structures, as well as encoding
15
+ Ruby objects to a NestedText file or string. Furthermore there is support for serialization
16
+ and deserialization of custom classes. Support for v3.2.1 of the data format will
17
+ all official tests passing.
18
+ email:
19
+ - erik.westrup@gmail.com
20
+ executables: []
21
+ extensions: []
22
+ extra_rdoc_files: []
23
+ files:
24
+ - ".editorconfig"
25
+ - ".gitignore"
26
+ - ".rubocop.yml"
27
+ - ".ruby-version"
28
+ - CHANGELOG.md
29
+ - CONTRIBUTING.md
30
+ - Gemfile
31
+ - LICENSE.txt
32
+ - OSSMETADATA
33
+ - README.md
34
+ - SECURITY.md
35
+ - lib/nestedtext.rb
36
+ - lib/nestedtext/constants.rb
37
+ - lib/nestedtext/core_ext.rb
38
+ - lib/nestedtext/decode.rb
39
+ - lib/nestedtext/dumper.rb
40
+ - lib/nestedtext/encode.rb
41
+ - lib/nestedtext/encode_helpers.rb
42
+ - lib/nestedtext/errors.rb
43
+ - lib/nestedtext/helpers.rb
44
+ - lib/nestedtext/parser.rb
45
+ - lib/nestedtext/scanners.rb
46
+ - lib/nestedtext/version.rb
47
+ - nestedtext.gemspec
48
+ homepage: https://github.com/erikw/nestedtext-ruby/
49
+ licenses:
50
+ - MIT
51
+ metadata:
52
+ homepage_uri: https://github.com/erikw/nestedtext-ruby/
53
+ source_code_uri: https://github.com/erikw/nestedtext-ruby/
54
+ changelog_uri: https://github.com/erikw/nestedtext-ruby/blob/main/CHANGELOG.md
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '3.0'
64
+ - - "<"
65
+ - !ruby/object:Gem::Version
66
+ version: '4'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.3.3
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: A ruby library for the human friendly data format NestedText https://nestedtext.org/
77
+ test_files: []