From 15469f24ee3f7fd34267d670a5242db9192ab3c5 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Wed, 5 Aug 2026 20:17:41 -0400 Subject: [PATCH 1/2] format: specify two-sorted (integer/bytes) expression evaluation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DRAFT for writer prose pass before the PR opens. Codifies the two-sorted semantics of pointer expressions: values are either unbounded non-negative integers (no width) or bytes (with a definite width). Arithmetic, JSON-number and odd-nibble-hex literals, $wordsize, and lookups are integers; even-nibble hex, $read, and $sizedN/$wordsized are bytes. An integer is accepted wherever an integer is expected (a bytes value is read as its big-endian integer value), but $concat and $keccak256 operands must be width-bearing bytes — a bare integer there is an error and must be resized first. Fixes the keccak256 example's bare-integer operand to be word-sized, matching what the mapping/string pointer examples already do. --- schemas/pointer/expression.schema.yaml | 83 ++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/schemas/pointer/expression.schema.yaml b/schemas/pointer/expression.schema.yaml index 21a1ff1612..c8215cc00d 100644 --- a/schemas/pointer/expression.schema.yaml +++ b/schemas/pointer/expression.schema.yaml @@ -5,6 +5,42 @@ title: ethdebug/format/pointer/expression description: | A schema for describing expressions that evaluate to values. + ## Two sorts of value: integers and bytes + + Every expression evaluates to a value of one of two sorts: + + - an **integer** — an unbounded, non-negative integer. It has a numeric + value but **no width**. Arithmetic is ordinary integer arithmetic. + - **bytes** — a finite sequence of bytes with a definite **width** (its + byte length). + + The two sorts are produced by different forms: + + - **Integers** are produced by a JSON-number literal, the `$wordsize` + constant, a variable or lookup (`.offset` / `.length` / `.slot`) that + denotes an index or count, an arithmetic operation (`$sum`, + `$difference`, `$product`, `$quotient`, `$remainder`), and a + hexadecimal literal that has an **odd** number of digits (which has no + whole-byte width — see `Literal`). + - **Bytes** are produced by a hexadecimal literal with an **even** number + of digits (its width is the number of bytes written), `$read` (its + width is the length of the region read), and the resize forms + `$sizedN` / `$wordsized` (whose width is `N` / the word size). + + ## Coercion and the width-bearing requirement + + Where an **integer** is expected — arithmetic operands, a list `count`, a + segment `slot` / `offset` / `length` — a bytes value is accepted and read + as the non-negative integer its bytes encode (big-endian). + + Where **bytes** are expected — the operands of `$concat` and `$keccak256`, + whose results depend on operand widths — the operand **must** be + width-bearing. A bare integer (a JSON number, an odd-digit hex literal, + `$wordsize`, an arithmetic result, or a lookup) is **not** valid there: + give it a width first with `$sizedN` or `$wordsized`. There is no implicit + widening of an integer to bytes; the resize forms are the only bridge from + an integer to bytes. + oneOf: - $ref: "#/$defs/Literal" - $ref: "#/$defs/Variable" @@ -20,7 +56,18 @@ $defs: Literal: title: Literal value description: | - An unsigned number or a `0x`-prefixed string of hexadecimal digits + A literal value, written either as a JSON number or as a `0x`-prefixed + hexadecimal string. + + Its sort follows its form: + + - a JSON number is an **integer** (no width); + - a hexadecimal string with an **even** number of digits is **bytes**, + whose width is the number of bytes written (`"0x00"` is one zero + byte, `"0xdead"` is two bytes); + - a hexadecimal string with an **odd** number of digits has no + whole-byte width and is therefore an **integer**, equal to the value + its digits denote (`"0x1"` is the integer `1`, not bytes). $ref: "schema:ethdebug/format/data/value" @@ -44,6 +91,12 @@ $defs: Arithmetic: title: Arithmetic operation + description: | + Ordinary integer arithmetic. Each operand is taken as an **integer** + (a bytes operand is read as the non-negative integer its bytes encode), + and the result is an **integer** with no width. To use an arithmetic + result where bytes are required, give it a width with `$sizedN` or + `$wordsized`. type: object properties: "$sum": @@ -113,7 +166,8 @@ $defs: An object of the form `{ ".": "" }`, to denote that this expression is equivalent to the defined value for the property named `` inside the region referenced as - ``. + ``. The value is an **integer** (a region's `.offset`, + `.length`, or `.slot`). `` **must** be a valid and present property on the corresponding region, or it **must** correspond to an optional property @@ -136,7 +190,8 @@ $defs: description: | An object of the form `{ "$read": "" }`. The value of this expression equals the raw bytes present in the running machine state - in the referenced region. + in the referenced region. The result is **bytes** whose width is the + length of the region read. type: object properties: $read: @@ -171,7 +226,14 @@ $defs: description: | An object of the form `{ "$keccak256": [...values] }`, indicating that this expression evaluates to the Solidity-style keccak256 hash of the - tightly-packed bytes encoded by `values`. + tightly-packed bytes encoded by `values`. The result is **bytes** of + width 32. + + Because the hash is taken over the concatenation of the operands' + bytes, each operand **must** be width-bearing (bytes): a bare integer + is not valid here and must be given a width first with `$sizedN` or + `$wordsized`. This is why a mapping-slot computation word-sizes its key + and slot before hashing. type: object properties: $keccak256: @@ -184,7 +246,7 @@ $defs: - $keccak256 examples: - $keccak256: - - 0 + - $wordsized: 0 - "0x00" Concat: @@ -193,7 +255,12 @@ $defs: An object of the form `{ "$concat": [...values] }`, indicating that this expression evaluates to the concatenation of bytes from each value. The byte width of each operand is preserved; no padding is added or - removed between operands. + removed between operands. The result is **bytes** whose width is the + sum of the operand widths. + + Each operand **must** be width-bearing (bytes): a bare integer is not + valid here and must be given a width first with `$sizedN` or + `$wordsized`. type: object properties: $concat: @@ -216,6 +283,10 @@ $defs: Resize: title: Resize data description: | + A resize operation produces **bytes** of a definite width, and is the + bridge from an integer to bytes: give it an integer (or bytes) and it + yields bytes of the requested width. + A resize operation expression is either an object of the form `{ "$sized": }` or an object of the form `{ "$wordsized": }`, where `` is an expression From e6cdad55dd3c7aaf6bba8ac6a857ba622d406fda Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Wed, 5 Aug 2026 20:21:59 -0400 Subject: [PATCH 2/2] format: tighten two-sorted expression prose for spec render --- schemas/pointer/expression.schema.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/schemas/pointer/expression.schema.yaml b/schemas/pointer/expression.schema.yaml index c8215cc00d..6823d0c279 100644 --- a/schemas/pointer/expression.schema.yaml +++ b/schemas/pointer/expression.schema.yaml @@ -37,9 +37,9 @@ description: | whose results depend on operand widths — the operand **must** be width-bearing. A bare integer (a JSON number, an odd-digit hex literal, `$wordsize`, an arithmetic result, or a lookup) is **not** valid there: - give it a width first with `$sizedN` or `$wordsized`. There is no implicit - widening of an integer to bytes; the resize forms are the only bridge from - an integer to bytes. + give it a width first with `$sizedN` or `$wordsized`. There is no + implicit widening; the resize forms are the only bridge from an integer + to bytes. oneOf: - $ref: "#/$defs/Literal" @@ -224,10 +224,10 @@ $defs: Keccak256: title: Keccak256 hash description: | - An object of the form `{ "$keccak256": [...values] }`, indicating that this - expression evaluates to the Solidity-style keccak256 hash of the - tightly-packed bytes encoded by `values`. The result is **bytes** of - width 32. + An object of the form `{ "$keccak256": [...values] }`, indicating + that this expression evaluates to the Solidity-style keccak256 hash + of the tightly-packed bytes encoded by `values`. The result is + **bytes** of width 32. Because the hash is taken over the concatenation of the operands' bytes, each operand **must** be width-bearing (bytes): a bare integer