ctx_parser 0.5.0 → 0.5.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d926ccda7788e5126e7dbd90230a48073eb13d275badfdb13c4f502f06407b8f
4
- data.tar.gz: 1fa99e3f02360e1eb9b77446e17156ca250214473a377c7d3e514bb71a32c1d9
3
+ metadata.gz: dfdec0a6366bcb4f65cca5025dacb968ec838de632dbed9b02424b1eadfe7ba9
4
+ data.tar.gz: c7b1eadc4fc4f6baa24b0e9c2c94e40080097e60da3ba25a6b4693c5d4735a42
5
5
  SHA512:
6
- metadata.gz: 93a927e92dfdb97cab57643787c7f5380fb11bf6a0bb5fd134fb89b03da0ab4c31c1b3d3b3c29f395b4b9d5884578800c3af0e94181df509441751a2f7c43dba
7
- data.tar.gz: 2b614f708cefbd0f9da209f9d2a9ea3bdb908ff87998e63dd35ecd7de32e0ea3d8f7259d0bef0538b478cbb0d998fe48f7a80ceaaf2546a8b0c306ede1e63747
6
+ metadata.gz: aa1e066001a118d610be8fd777844ddd53dab4627ab976eefaf076218d7ec77d62d15011ae203804cb8c3117f03cee079aa83315cd909bb2eaae192817201d4f
7
+ data.tar.gz: 0e1f197af6d134e9fa86989b62b4e82e6b4cecc103336a213128b211f1c35060e337903c04b91cd8f365729ce602a8f18acdba5cb1d7409efef01eb2ef07ca33
data/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ Changes to the public contract are recorded here. Versions follow Semantic Versi
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.5.1 (2026-10-30)
8
+
9
+ - Add `Document#build_blocks` for native syntax trees and `Document::Flat` for independent command lines, sharing initialization, finalization and root validation.
10
+ - Validate Block children by source ownership, containment and order; indentation remains the responsibility of the default tree builder.
11
+ - Add cached `Cursor#block(index)` source ranges whose state stays live across rewind and reset.
12
+
7
13
  ## 0.5.0 (2026-10-30)
8
14
 
9
15
  - Add Cursor#resolved? with a lazy, invalidated prefix index; Block/Capture completeness remains live after state changes and reset.
data/README-zh.md CHANGED
@@ -94,6 +94,8 @@ result.records.each { |row| puts "#{row[:name]}: #{row[:state]}" }
94
94
 
95
95
  `Records` 持有自己的筛选数组,行对象仍属于调用方;使用期间应保持行不变,需要快照时传入 `Immutable.copy(rows)`。`Immutable.copy` 即使遇到冻结容器也重建普通容器,单次复制中保留共享引用并拒绝循环;不扫描方法表,也不引入应用级全局缓存。字符串经 `String.new` 和 Ruby 去重后冻结,保留编码与字节。不同次复制不保证容器对象身份相同;`Immutable.copy_string` 提供相同的文本边界并省去容器遍历表。整数转换仅在 `min` 允许负数或为 `nil` 时接受负号,默认仍只接受非负数。
96
96
 
97
+ `Document` 默认按缩进建树;原生语法可以覆盖受保护的 `build_blocks`,返回同一游标内按来源排序、互不重叠的根 Block,随后仍执行 `finish`。子块必须处于父范围内并保持顺序,不要求更深缩进。`Document::Flat` 将命令行作为独立来源块;`cursor.block(index)` 复用单行范围,rewind/reset 保留其身份,完整性始终读取当前游标状态。这些 API 由配套 0.5.1 候选提供,厂商语法由消费模块拥有。
98
+
97
99
  ## 开发与测试
98
100
 
99
101
  共享类按实际职责命名,平台信息放在命名空间中。直接对应设备命令的解析器保留文件名和类名。
data/README.md CHANGED
@@ -94,6 +94,8 @@ Address conversion uses `Values.ip_address`, `Values.ip_address?`, and `Values.m
94
94
 
95
95
  `Records` owns selection arrays, while row objects remain caller-owned: treat those rows as stable during use, or pass `Immutable.copy(rows)` when a snapshot is needed. `Immutable.copy` rebuilds containers even when frozen, preserves shared references within each copy, and rejects cycles. This removes attached state, Hash defaults and singleton behavior without scanning method tables or maintaining a global cache. String snapshots use `String.new` followed by Ruby string deduplication; encoding and bytes are preserved. Container identity is not preserved across separate copies. `Immutable.copy_string` provides the same text boundary without allocating a container traversal map. Integer conversion accepts a minus sign only when `min` permits negative values (or is `nil`); the default remains nonnegative.
96
96
 
97
+ `Document` builds indentation-based trees by default. Native grammars may override protected `build_blocks` and return ordered, non-overlapping root Blocks from the same cursor; `finish` still runs afterward. Block children must be contained and ordered, but need not have greater indentation. `Document::Flat` treats each command as an independent source block. `cursor.block(index)` reuses a one-line range; rewind/reset preserve its identity while completeness follows current cursor state. These APIs are provided by the paired 0.5.1 candidate; vendor grammar remains in consumers.
98
+
97
99
  ## Development and tests
98
100
 
99
101
  Name shared classes by their responsibility and use the namespace for platform context. Preserve filenames and class names that directly match device commands.
@@ -21,7 +21,7 @@ module CtxParser
21
21
  previous_index = header.index
22
22
  unless children.is_a?(Array) && children.all? do |child|
23
23
  next false unless child.is_a?(Block) && child.cursor.equal?(cursor) &&
24
- child.index > previous_index && child.last_index <= last_index && child.header.indent > header.indent
24
+ child.index > previous_index && child.last_index <= last_index
25
25
 
26
26
  previous_index = child.last_index
27
27
  true
@@ -25,12 +25,20 @@ module CtxParser
25
25
  @lines << Line.new(index: @lines.size, raw: raw.freeze)
26
26
  end
27
27
  @lines.freeze
28
+ @line_blocks = {}
28
29
  @checksum = Digest::SHA256.hexdigest(@content).freeze
29
30
  reset!
30
31
  end
31
32
 
32
33
  # 返回原始来源行数。
33
34
  def size = lines.size
35
+
36
+ # 单行范围按需复用;回退和重置只改变状态,不改变不可变来源块。
37
+ def block(index)
38
+ validate_index!(index)
39
+ require_relative "block" unless defined?(Block)
40
+ @line_blocks[index] ||= Block.new(cursor: self, header: lines[index], children: [], last_index: index)
41
+ end
34
42
  # 判断游标是否到达来源末尾。
35
43
  def eof? = position >= size
36
44
  # 查看下一来源行,不推进游标也不修改状态。
@@ -4,7 +4,7 @@ require_relative "cursor"
4
4
  require_relative "block"
5
5
 
6
6
  module CtxParser
7
- # 持有配置树及诊断;分类器负责语法,Block 负责缩进范围。
7
+ # 持有配置树及诊断;构建器解释语法,Block 只约束来源范围。
8
8
  class Document
9
9
  attr_reader :cursor, :roots, :issues
10
10
 
@@ -18,7 +18,8 @@ module CtxParser
18
18
  end
19
19
  @cursor = source.is_a?(Cursor) ? source : Cursor.new(source, max_bytes: max_bytes)
20
20
  @comments, @boundaries, @issues = comments, boundaries, []
21
- @roots = Block.read(cursor) { |line| classify(line) }
21
+ cursor.rewind
22
+ @roots = validate_roots(build_blocks)
22
23
  finish
23
24
  @issues = Immutable.copy(@issues)
24
25
  end
@@ -33,6 +34,9 @@ module CtxParser
33
34
 
34
35
  protected
35
36
 
37
+ # 默认按缩进构建;原生语法可覆盖此步骤,继续共享来源初始化和校验。
38
+ def build_blocks = Block.read(cursor) { |line| classify(line) }
39
+
36
40
  # 允许有状态词法分析器将行分类为配置条目、忽略行或边界。
37
41
  def classify(line)
38
42
  return :boundary if @boundaries&.match?(line.text)
@@ -43,5 +47,39 @@ module CtxParser
43
47
 
44
48
  # 允许词法分析器在文档结束时报告未闭合上下文,不向公共配置块添加词法状态。
45
49
  def finish; end
50
+
51
+ private
52
+
53
+ # 根块可以留出注释或边界行,但不能重叠、倒序或引用别的来源。
54
+ def validate_roots(entries)
55
+ entries = Array.new(entries) if entries.is_a?(Array)
56
+ previous = -1
57
+ unless entries.is_a?(Array) && entries.all? do |entry|
58
+ next false unless entry.is_a?(Block) && entry.cursor.equal?(cursor) && entry.index > previous
59
+
60
+ previous = entry.last_index
61
+ true
62
+ end
63
+ raise ArgumentError, "Root blocks must be ordered within this document"
64
+ end
65
+ entries.freeze
66
+ end
67
+
68
+ # set 等逐行配置没有缩进父子关系,每行独立保留来源状态。
69
+ class Flat < Document
70
+ protected
71
+
72
+ def build_blocks
73
+ entries = []
74
+ while (line = cursor.read)
75
+ case classify(line)
76
+ when :entry then entries << cursor.block(line.index)
77
+ when :ignored, :boundary then cursor.mark(line.index, state: :ignored)
78
+ else raise ArgumentError, "Invalid source line disposition"
79
+ end
80
+ end
81
+ entries
82
+ end
83
+ end
46
84
  end
47
85
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module CtxParser
4
4
  # 公开包版本,供 RubyGems 元数据和运行时调用方共用。
5
- VERSION = "0.5.0"
5
+ VERSION = "0.5.1"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ctx_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gatework contributors