sprawling-documents 0.0.12

The rules of a document a person reads and edits through a sprawling city's page: versions, characters, windows and saves.
Documentation
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-- Copyright (c) 2026 2youg1 and the sprawling contributors

/-!
# encoding:一个版本的字节拼出哪些字符

规定 `crates/documents/src/encoding.rs`(`documents::Encoding`、`Reading::of`、`Encoding::of_mark`、`Encoding::decode`)。Rust 代码是「怎样守住」的权威;本模型是「必须守住哪些性质」的权威。

字节顺序标记先判:有标记就照标记读,没有就是 UTF-8 或什么都不是(D4)。某串字节在 UTF-8、UTF-16LE、UTF-16BE 下是否合法是参数 `Validity`:那是 Unicode 的事实,Rust 用标准库的 `str::from_utf8` 与 `char::decode_utf16` 判它,模型不重述。

证明的性质:

1. **带标记的 UTF-16 是文本,不管里面有多少 NUL**(`marked_utf16le_is_text`):旧的判法凭 NUL 判二进制,正好把每个 ASCII 字符旁边都有一个 NUL 的 UTF-16 文件判成二进制(refrain 路线图附录 C)。
2. **没有标记而含 NUL 的不是文本**(`nul_without_mark_is_opaque`):文本文件不含 NUL,城自己存的东西(redb、CAS 里的块)含。
3. **判成文本的,在它的编码下拼得出**(`text_is_spelled`):判定与解码读同一条规则,所以一个判成文本的版本,它的每个窗口都解得出(窗口切在字符边界上,`spec/Window.lean`)。

**照旧的 NUL 判法,带标记的 UTF-16 文件就成了不透明的**(`nulJudgement_calls_wide_text_opaque`):本模型咬得动的演示。
-/

namespace Documents.Encoding

inductive Encoding where
  | utf8
  | utf8Bom
  | utf16Le
  | utf16Be
  deriving DecidableEq, Repr

inductive Reading where
  | text (encoding : Encoding)
  | opaque
  deriving DecidableEq, Repr

/-- 三种编码各自的合法性,由 Unicode 定义,是模型的参数。 -/
structure Validity where
  utf8 : List Nat → Bool
  utf16Le : List Nat → Bool
  utf16Be : List Nat → Bool

/-- 前三个字节里的标记说的编码;没有标记就是 UTF-8。 -/
def ofMark : List Nat → Encoding
  | 0xEF :: 0xBB :: 0xBF :: _ => .utf8Bom
  | 0xFF :: 0xFE :: _ => .utf16Le
  | 0xFE :: 0xFF :: _ => .utf16Be
  | _ => .utf8

/-- 一串字节在这种编码下拼不拼得出文本。 -/
def spells (v : Validity) : Encoding → List Nat → Bool
  | .utf8, s => v.utf8 s && !s.contains 0
  | .utf8Bom, s => v.utf8 s
  | .utf16Le, s => v.utf16Le s
  | .utf16Be, s => v.utf16Be s

/-- `Reading::of`:标记先判,再看字节拼不拼得出。 -/
def reading (v : Validity) (s : List Nat) : Reading :=
  if spells v (ofMark s) s then .text (ofMark s) else .opaque

theorem marked_utf16le_is_text (v : Validity) (rest : List Nat)
    (valid : v.utf16Le (0xFF :: 0xFE :: rest) = true) :
    reading v (0xFF :: 0xFE :: rest) = .text .utf16Le := by
  simp [reading, ofMark, spells, valid]

theorem nul_without_mark_is_opaque (v : Validity) (s : List Nat)
    (unmarked : ofMark s = .utf8) (nul : 0 ∈ s) : reading v s = .opaque := by
  simp [reading, unmarked, spells, nul]

theorem text_is_spelled (v : Validity) (s : List Nat) (e : Encoding)
    (judged : reading v s = .text e) : spells v e s = true := by
  unfold reading at judged
  split at judged
  · rename_i spelled
    cases judged
    exact spelled
  · cases judged

/-!
## 咬得动的演示

旧的判法:含 NUL 就是二进制,否则当 UTF-8 读。一个带标记的 UTF-16LE 文件 `"a"` 是 `FF FE 61 00`,它被判成不透明的。
-/

def nulJudgement (s : List Nat) : Reading :=
  if s.contains 0 then .opaque else .text .utf8

theorem nulJudgement_calls_wide_text_opaque :
    nulJudgement [0xFF, 0xFE, 0x61, 0x00] = .opaque := by
  decide

end Documents.Encoding