diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d22ca0d4..1c3d06ae2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,15 @@ history. ## Unreleased +- **BREAKING CHANGES:** `fjs/media/json/parser` migrates from authored + TypeScript (`.f.ts`) to JSDoc-typed JavaScript (`.f.mjs`) under the + stage-1 TypeScript-to-mjs migration, splitting `_JsonObject`, + `_JsonArray`, `_JsonStack`, `_StateParse`, and `_JsonState` into a + sibling `types.ts` — importers must use the `.f.mjs` specifier for the + runtime value and the `types.ts` specifier for the types. `proof.f.ts` + stays TypeScript for now (it depends on `fjs/media/json/module.f.ts`, + still unmigrated) + [#1499](https://github.com/functionalscript/functionalscript/pull/1499) - **BREAKING CHANGES:** the JSON rtti schemas `primitive`, `unknown`, `object`, and `array` move from `fjs/media/json/module.f.ts` to a new JSDoc-typed `fjs/media/json/rtti/module.f.mjs`, and the `Primitive`, diff --git a/fjs/asn.1/module.f.mjs b/fjs/asn.1/module.f.mjs index 3b7316f0f..9da03a9ad 100644 --- a/fjs/asn.1/module.f.mjs +++ b/fjs/asn.1/module.f.mjs @@ -3,10 +3,12 @@ * helpers, length-prefixed payloads, and OID conversion via Base-128. * * @module + * + * @import { Unpacked, Vec } from '../types/bit_vec/types.ts' + * @import { ObjectIdentifier, Raw, Record, Sequence, SupportedRecord, _Tag } from './types.ts' */ import { bitLength, divUp8 } from '../types/bigint/module.f.mjs' - import { empty, isVec, @@ -17,19 +19,8 @@ import { vec, vec8, } from '../types/bit_vec/module.f.mjs' - -/** @import { Unpacked, Vec } from '../types/bit_vec/types.ts' */ - -/** - * @import { - * ObjectIdentifier, Raw, Record, Sequence, SupportedRecord, _Tag, - * } from './types.ts' - */ - import { identity } from '../types/function/module.f.mjs' - import { max } from '../types/function/compare/module.f.mjs' - import { encode as b128encode, decode as b128decode } from '../basen/base128/module.f.mjs' // diff --git a/fjs/asn.1/proof.f.mjs b/fjs/asn.1/proof.f.mjs index b60271924..bd47ad005 100644 --- a/fjs/asn.1/proof.f.mjs +++ b/fjs/asn.1/proof.f.mjs @@ -1,5 +1,5 @@ -import { empty, isVec, length, msb, uint, unpack, vec, vec8 } from '../types/bit_vec/module.f.mjs' /** @import { Vec } from '../types/bit_vec/types.ts' */ +import { empty, isVec, length, msb, uint, unpack, vec, vec8 } from '../types/bit_vec/module.f.mjs' import { asBase } from '../types/nominal/module.f.mjs' diff --git a/fjs/basen/base128/module.f.mjs b/fjs/basen/base128/module.f.mjs index 0d59fcb5b..87541bcd3 100644 --- a/fjs/basen/base128/module.f.mjs +++ b/fjs/basen/base128/module.f.mjs @@ -2,10 +2,11 @@ * Base-128 encoding and decoding utilities over bit vectors. * * @module + * + * @import { Vec } from '../../types/bit_vec/types.ts' */ import { vec8, msb, empty } from '../../types/bit_vec/module.f.mjs' -/** @import { Vec } from '../../types/bit_vec/types.ts' */ const { concat, popFront } = msb diff --git a/fjs/basen/base128/proof.f.mjs b/fjs/basen/base128/proof.f.mjs index 0e1a7d3a6..84ef10528 100644 --- a/fjs/basen/base128/proof.f.mjs +++ b/fjs/basen/base128/proof.f.mjs @@ -1,8 +1,7 @@ -import { empty, vec, vec8 } from '../../types/bit_vec/module.f.mjs' /** @import { Vec } from '../../types/bit_vec/types.ts' */ +import { empty, vec, vec8 } from '../../types/bit_vec/module.f.mjs' import { asBase } from '../../types/nominal/module.f.mjs' - import { encode, decode } from './module.f.mjs' // diff --git a/fjs/basen/base64/module.f.mjs b/fjs/basen/base64/module.f.mjs index 37d79fe4e..25f8cae2f 100644 --- a/fjs/basen/base64/module.f.mjs +++ b/fjs/basen/base64/module.f.mjs @@ -2,13 +2,12 @@ * Standard Base64 encoding and decoding (RFC 4648). * * @module + * + * @import { Vec } from '../../types/bit_vec/types.ts' + * @import { Nullable } from '../../types/nullable/types.ts' */ import { msb, length, vec, maxLength } from '../../types/bit_vec/module.f.mjs' -/** @import { Vec } from '../../types/bit_vec/types.ts' */ - -/** @import { Nullable } from '../../types/nullable/types.ts' */ - import { baseN } from "../module.f.mjs" const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' diff --git a/fjs/basen/base64/proof.f.mjs b/fjs/basen/base64/proof.f.mjs index c71534f12..78a437270 100644 --- a/fjs/basen/base64/proof.f.mjs +++ b/fjs/basen/base64/proof.f.mjs @@ -1,8 +1,7 @@ -import { assertEq } from '../../asserts/module.f.mjs' - -import { empty, vec, repeat, vec8, maxLength } from '../../types/bit_vec/module.f.mjs' /** @import { Vec } from '../../types/bit_vec/types.ts' */ +import { assertEq } from '../../asserts/module.f.mjs' +import { empty, vec, repeat, vec8, maxLength } from '../../types/bit_vec/module.f.mjs' import { encode, decode } from './module.f.mjs' /** diff --git a/fjs/basen/cbase32/module.f.mjs b/fjs/basen/cbase32/module.f.mjs index 172593cd9..d8ab0488d 100644 --- a/fjs/basen/cbase32/module.f.mjs +++ b/fjs/basen/cbase32/module.f.mjs @@ -2,13 +2,12 @@ * Content-addressable Base32 encoding and decoding helpers. * * @module + * + * @import { Vec } from '../../types/bit_vec/types.ts' + * @import { Nullable } from '../../types/nullable/types.ts' */ import { msb, lsb, length, vec, empty } from '../../types/bit_vec/module.f.mjs' -/** @import { Vec } from '../../types/bit_vec/types.ts' */ - -/** @import { Nullable } from '../../types/nullable/types.ts' */ - import { baseN } from '../module.f.mjs' // 0123456789abcdef diff --git a/fjs/basen/cbase32/proof.f.mjs b/fjs/basen/cbase32/proof.f.mjs index 8aa1aeabb..44bee687c 100644 --- a/fjs/basen/cbase32/proof.f.mjs +++ b/fjs/basen/cbase32/proof.f.mjs @@ -1,8 +1,7 @@ -import { empty, vec } from '../../types/bit_vec/module.f.mjs' /** @import { Vec } from '../../types/bit_vec/types.ts' */ +import { empty, vec } from '../../types/bit_vec/module.f.mjs' import { cBase32ToVec, cBase32ToVec5x, vec5xToCBase32, vecToCBase32 } from './module.f.mjs' - import { assertEq } from '../../asserts/module.f.mjs' // diff --git a/fjs/basen/module.f.mjs b/fjs/basen/module.f.mjs index 166ea3e48..7d1259fc7 100644 --- a/fjs/basen/module.f.mjs +++ b/fjs/basen/module.f.mjs @@ -10,18 +10,16 @@ * `vecToString` / `stringToVec`. * * @module + * + * @import { Vec } from '../types/bit_vec/types.ts' + * @import { List } from '../types/list/types.ts' + * @import { BaseN } from './types.ts' */ import { msb, lsb, vec, chunkList, unpack } from '../types/bit_vec/module.f.mjs' -/** @import { Vec } from '../types/bit_vec/types.ts' */ - import { fold } from '../types/list/module.f.mjs' -/** @import { List } from '../types/list/types.ts' */ - import { compose } from '../types/function/module.f.mjs' -/** @import { BaseN } from './types.ts' */ - const { unpackSplit } = msb const { tryListToVec: reversedListToVec } = lsb diff --git a/fjs/bnf/data/module.f.mjs b/fjs/bnf/data/module.f.mjs index 720f5d7ce..826358e06 100644 --- a/fjs/bnf/data/module.f.mjs +++ b/fjs/bnf/data/module.f.mjs @@ -11,16 +11,16 @@ * See `./types.ts` for the type-level API. * * @module + * + * @import { DataRule, Rule as FRule, Sequence as FSequence } from '../types.ts' + * @import { StringMap } from '../../types/object/types.ts' + * @import { EmptyTag, Rule, RuleSet, Sequence, Variant } from './types.ts' */ + import { stringToCodePointList } from '../../text/utf16/module.f.mjs' import { map, toArray } from '../../types/list/module.f.mjs' -import { - oneEncode, -} from '../module.f.mjs' -/** @import { DataRule, Rule as FRule, Sequence as FSequence } from '../types.ts' */ +import { oneEncode } from '../module.f.mjs' import { definedEntries } from '../../types/object/module.f.mjs' -/** @import { StringMap } from '../../types/object/types.ts' */ -/** @import { EmptyTag, Rule, RuleSet, Sequence, Variant } from './types.ts' */ /** @typedef {StringMap} _EmptyTagMap */ diff --git a/fjs/bnf/data/proof.f.ts b/fjs/bnf/data/proof.f.ts index f41d8ae06..dde48f392 100644 --- a/fjs/bnf/data/proof.f.ts +++ b/fjs/bnf/data/proof.f.ts @@ -1,12 +1,14 @@ -import { stringify } from '../../media/json/module.f.ts' +import type { RuleSet } from './types.ts' + import { identity } from '../../types/function/module.f.mjs' import { sort } from '../../types/object/module.f.mjs' import { oneEncode, option, range, rangeDecode, repeat0Plus, set } from '../module.f.mjs' -import { classic, deterministic } from '../testlib.f.ts' import { emptyTagMap, toData } from './module.f.mjs' -import type { RuleSet } from './types.ts' import { assertEq } from '../../asserts/module.f.mjs' +import { stringify } from '../../media/json/module.f.ts' +import { classic, deterministic } from '../testlib.f.ts' + export const proof = { rangeDecode: () => { const decoded1 = stringify(sort)(rangeDecode(0x000079_000087)) diff --git a/fjs/bnf/descent/module.f.mjs b/fjs/bnf/descent/module.f.mjs index 7a2587bf1..871f714eb 100644 --- a/fjs/bnf/descent/module.f.mjs +++ b/fjs/bnf/descent/module.f.mjs @@ -14,15 +14,17 @@ * See `./types.ts` for the type-level API. * * @module + * + * @import { TerminalRange } from '../types.ts' + * @import { Rule as DataRule, Sequence } from '../data/types.ts' + * @import { Rule as FRule } from '../types.ts' + * @import { AstTag, AstSequenceMeta, DescentFailure, DescentMatch, DescentMatchResult, DescentMatchRule } from './types.ts' */ + import { rangeDecode } from '../module.f.mjs' -/** @import { TerminalRange } from '../types.ts' */ import { contains as rangeContains } from '../../types/range/module.f.mjs' import { definedEntries } from '../../types/object/module.f.mjs' import { emptyTagMap, toData } from '../data/module.f.mjs' -/** @import { Rule as DataRule, Sequence } from '../data/types.ts' */ -/** @import { Rule as FRule } from '../types.ts' */ -/** @import { AstTag, AstSequenceMeta, DescentFailure, DescentMatch, DescentMatchResult, DescentMatchRule } from './types.ts' */ /** * Folds one rejected terminal into the furthest-failure record: further along diff --git a/fjs/bnf/descent/proof.f.ts b/fjs/bnf/descent/proof.f.ts index 6948dc47a..0963de37f 100644 --- a/fjs/bnf/descent/proof.f.ts +++ b/fjs/bnf/descent/proof.f.ts @@ -1,13 +1,15 @@ import type { CodePoint } from '../../text/utf16/types.ts' +import type { DescentMatch, CodePointMeta, DescentMatchResult } from './types.ts' + import { stringToCodePointList } from '../../text/utf16/module.f.mjs' import { map, toArray } from '../../types/list/module.f.mjs' import { commaJoin0Plus, option, range, repeat0Plus, set } from '../module.f.mjs' -import { deterministic } from '../testlib.f.ts' import { emptyTagMap, toData } from '../data/module.f.mjs' import { descentParser } from './module.f.mjs' -import type { DescentMatch, CodePointMeta, DescentMatchResult } from './types.ts' import { assertEq, assertNotNullish } from '../../asserts/module.f.mjs' +import { deterministic } from '../testlib.f.ts' + const mapCodePoint = (cp: CodePoint): CodePointMeta => [cp, undefined] // The code point of a one-character string, for expectations that would diff --git a/fjs/bnf/ll1/module.f.mjs b/fjs/bnf/ll1/module.f.mjs index 8767a7580..c2bc6261e 100644 --- a/fjs/bnf/ll1/module.f.mjs +++ b/fjs/bnf/ll1/module.f.mjs @@ -11,19 +11,21 @@ * See `./types.ts` for the type-level API. * * @module + * + * @import { Properties } from '../../types/range_map/types.ts' + * @import { StringSet } from '../../types/string_set/types.ts' + * @import { EmptyTag, RuleSet } from '../data/types.ts' + * @import { Rule as FRule } from '../types.ts' + * @import { AstSequence, AstTag, Match, MatchResult, MatchRule, Remainder, _Dispatch, _DispatchMap, _DispatchResult, _DispatchRule } from './types.ts' */ + import { strictEqual } from '../../types/function/operator/module.f.mjs' import { toArray } from '../../types/list/module.f.mjs' import { rangeMap } from '../../types/range_map/module.f.mjs' -/** @import { Properties } from '../../types/range_map/types.ts' */ import { contains, set } from '../../types/string_set/module.f.mjs' -/** @import { StringSet } from '../../types/string_set/types.ts' */ import { rangeDecode } from '../module.f.mjs' import { definedEntries } from '../../types/object/module.f.mjs' import { emptyTagMap, toData } from '../data/module.f.mjs' -/** @import { EmptyTag, RuleSet } from '../data/types.ts' */ -/** @import { Rule as FRule } from '../types.ts' */ -/** @import { AstSequence, AstTag, Match, MatchResult, MatchRule, Remainder, _Dispatch, _DispatchMap, _DispatchResult, _DispatchRule } from './types.ts' */ /** @type {Properties<_DispatchResult>} */ const dispatchProps = { diff --git a/fjs/bnf/ll1/proof.f.ts b/fjs/bnf/ll1/proof.f.ts index d483aec93..2e636e15a 100644 --- a/fjs/bnf/ll1/proof.f.ts +++ b/fjs/bnf/ll1/proof.f.ts @@ -1,13 +1,15 @@ +import type { RuleSet } from '../data/types.ts' +import type { MatchResult } from './types.ts' + import { stringToCodePointList } from '../../text/utf16/module.f.mjs' import { toArray } from '../../types/list/module.f.mjs' import { commaJoin0Plus, option, range, repeat0Plus, set } from '../module.f.mjs' -import { deterministic } from '../testlib.f.ts' import { toData } from '../data/module.f.mjs' -import type { RuleSet } from '../data/types.ts' import { dispatchMap, parser, parserRuleSet } from './module.f.mjs' -import type { MatchResult } from './types.ts' import { assertEq } from '../../asserts/module.f.mjs' +import { deterministic } from '../testlib.f.ts' + export const proof = { dispatch: [ () => { diff --git a/fjs/bnf/module.f.mjs b/fjs/bnf/module.f.mjs index dfa13b9b7..ff074cd22 100644 --- a/fjs/bnf/module.f.mjs +++ b/fjs/bnf/module.f.mjs @@ -6,17 +6,17 @@ * See `./types.ts` for the type-level API. * * @module + * + * @import { Tuple } from '../types/array/types.ts' + * @import { TerminalRange, Sequence, Rule, RangeVariant, None, Option, Repeat0Plus, Repeat1Plus, Join1Plus, Repeat } from './types.ts' */ -/** @import { Tuple } from '../types/array/types.ts' */ - import { codePointListToString, stringToCodePointList } from '../text/utf16/module.f.mjs' import { definedValues } from '../types/object/module.f.mjs' import { isTuple } from '../types/array/module.f.mjs' import { map, toArray, repeat as listRepeat } from '../types/list/module.f.mjs' import { contains } from '../types/range/module.f.mjs' import { assert } from '../asserts/module.f.mjs' -/** @import { TerminalRange, Sequence, Variant, DataRule, LazyRule, Rule, RangeVariant, None, Option, Repeat0Plus, Repeat1Plus, Join1Plus, Join0Plus, Repeat } from './types.ts' */ // Internals: diff --git a/fjs/bnf/proof.f.ts b/fjs/bnf/proof.f.ts index 67d46c600..fd2468a40 100644 --- a/fjs/bnf/proof.f.ts +++ b/fjs/bnf/proof.f.ts @@ -1,5 +1,6 @@ +import type { Rule } from './types.ts' + import { assert, assertEq } from '../asserts/module.f.mjs' -import { classic, deterministic } from './testlib.f.ts' import { rangeEncode, str, @@ -10,7 +11,8 @@ import { oneEncode, repeat1Plus, } from './module.f.mjs' -import type { Rule } from './types.ts' + +import { classic, deterministic } from './testlib.f.ts' export const proof = { test: () => { diff --git a/fjs/bnf/testlib.f.ts b/fjs/bnf/testlib.f.ts index 191f889ca..c3e4e9b13 100644 --- a/fjs/bnf/testlib.f.ts +++ b/fjs/bnf/testlib.f.ts @@ -1,3 +1,5 @@ +import type { Rule } from './types.ts' + import { commaJoin0Plus, none, @@ -9,7 +11,6 @@ import { set, unicodeMax, } from './module.f.mjs' -import type { Rule } from './types.ts' export const classic = (): Rule => { diff --git a/fjs/bnf/token_symbol/module.f.mjs b/fjs/bnf/token_symbol/module.f.mjs index 9950e0506..02adbc5c1 100644 --- a/fjs/bnf/token_symbol/module.f.mjs +++ b/fjs/bnf/token_symbol/module.f.mjs @@ -9,11 +9,13 @@ * See `./types.ts` for the `Encoding` type-level API. * * @module + * + * @import { Encoding } from './types.ts' */ + import { assert } from '../../asserts/module.f.mjs' import { fromUndefined } from '../../types/nullable/module.f.mjs' import { eof, rangeDecode, unicodeRange } from '../module.f.mjs' -/** @import { Encoding } from './types.ts' */ const [, unicodeLast] = rangeDecode(unicodeRange) diff --git a/fjs/bnf/types.ts b/fjs/bnf/types.ts index 7803a0d53..dae80ca96 100644 --- a/fjs/bnf/types.ts +++ b/fjs/bnf/types.ts @@ -3,6 +3,7 @@ * * @module */ + import type { StringMap } from '../types/object/types.ts' /** diff --git a/fjs/cas/cli/module.f.mjs b/fjs/cas/cli/module.f.mjs index 1d33e77fa..516e4879c 100644 --- a/fjs/cas/cli/module.f.mjs +++ b/fjs/cas/cli/module.f.mjs @@ -2,18 +2,19 @@ * CAS CLI command handlers. * * @module + * + * @import { All, Read, Write, WriteFile } from '../../effects/node/types.ts' + * @import { Commands } from '../../cli/types.ts' + * @import { MemOp } from '../../effects/memory/types.ts' + * @import { FileCasOperation } from '../types.ts' */ import { sha256 } from '../../crypto/sha2/module.f.mjs' import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.mjs' import { forEachStep, pure, step } from '../../effects/module.f.mjs' import { errorExit, log, writeFromStream } from '../../effects/node/module.f.mjs' -/** @import { All, Read, Write, WriteFile } from '../../effects/node/types.ts' */ import { dispatch } from '../../cli/module.f.mjs' -/** @import { Commands } from '../../cli/types.ts' */ -/** @import { MemOp } from '../../effects/memory/types.ts' */ import { casAddFile, fileCas } from '../module.f.mjs' -/** @import { FileCasOperation } from '../types.ts' */ /** @type {Commands} */ export const commands = [ diff --git a/fjs/cas/cli/proof.f.mjs b/fjs/cas/cli/proof.f.mjs index 772237359..f6a3f8f71 100644 --- a/fjs/cas/cli/proof.f.mjs +++ b/fjs/cas/cli/proof.f.mjs @@ -1,8 +1,9 @@ +/** @import { NodeProgramOptions } from '../../effects/node/types.ts' */ + import { commands } from './module.f.mjs' import { computeSync, sha256 } from '../../crypto/sha2/module.f.mjs' import { maxLength, vec, vec8 } from '../../types/bit_vec/module.f.mjs' import { defaultNodeProgramOptions, emptyState, virtual } from '../../effects/node/virtual/module.f.mjs' -/** @import { NodeProgramOptions } from '../../effects/node/types.ts' */ import { dispatch } from '../../cli/module.f.mjs' import { vecToCBase32 } from '../../basen/cbase32/module.f.mjs' import { assert, assertEq } from '../../asserts/module.f.mjs' diff --git a/fjs/cas/evo/module.f.ts b/fjs/cas/evo/module.f.ts index c6b5b4513..2ecde3e95 100644 --- a/fjs/cas/evo/module.f.ts +++ b/fjs/cas/evo/module.f.ts @@ -42,26 +42,29 @@ * * @module */ -import { pure, foldStep } from '../../effects/module.f.mjs' + import type { Effect, Operation } from '../../effects/types.ts' +import type { Key, MemOp } from '../../effects/memory/types.ts' +import type { Cas } from '../types.ts' +import type { Ok, Result } from '../../types/result/types.ts' +import type { StringMap } from '../../types/object/types.ts' +import type { Vec } from '../../types/bit_vec/types.ts' +import type { IoResult } from '../../effects/node/types.ts' + +import { pure, foldStep } from '../../effects/module.f.mjs' import { eff } from '../../effects/eff/module.f.mjs' import { create, read, write } from '../../effects/memory/module.f.mjs' -import type { Key, MemOp } from '../../effects/memory/types.ts' import { collectRead } from '../module.f.mjs' -import type { Cas } from '../types.ts' import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.mjs' import { fromVec } from '../../text/utf8/module.f.mjs' import { tryUtf8 } from '../../text/module.f.mjs' -import { decodeText, encodeText, dialect, checkReferences, isHash, type LockMap, type Revision } from '../../media/revision/module.f.ts' -import type { Ok, Result } from '../../types/result/types.ts' import { ok, error } from '../../types/result/module.f.mjs' import { nonEmpty, empty as elEmpty } from '../../effects/list/module.f.mjs' import { at, definedEntries } from '../../types/object/module.f.mjs' -import type { StringMap } from '../../types/object/types.ts' import { unwrap } from '../../types/nullable/module.f.mjs' -import type { Vec } from '../../types/bit_vec/types.ts' import { isNotFound } from '../../effects/node/module.f.mjs' -import type { IoResult } from '../../effects/node/types.ts' + +import { decodeText, encodeText, dialect, checkReferences, isHash, type LockMap, type Revision } from '../../media/revision/module.f.ts' /** A cBase32 content hash, as accepted/returned by `Cas`. */ export type Hash = string diff --git a/fjs/cas/evo/proof.f.ts b/fjs/cas/evo/proof.f.ts index d60962a1c..a07f2ae7e 100644 --- a/fjs/cas/evo/proof.f.ts +++ b/fjs/cas/evo/proof.f.ts @@ -1,18 +1,20 @@ +import type { Cas } from '../types.ts' +import type { Vec } from '../../types/bit_vec/types.ts' +import type { Ok } from '../../types/result/types.ts' +import type { IoResult } from '../../effects/node/types.ts' + import { assert, assertEq } from '../../asserts/module.f.mjs' import { pure } from '../../effects/module.f.mjs' import { fileCas } from '../module.f.mjs' -import type { Cas } from '../types.ts' import { sha256 } from '../../crypto/sha2/module.f.mjs' import { emptyState, virtual } from '../../effects/node/virtual/module.f.mjs' -import type { Vec } from '../../types/bit_vec/types.ts' import { vec, vec8 } from '../../types/bit_vec/module.f.mjs' import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.mjs' import { unwrap } from '../../types/nullable/module.f.mjs' -import type { Ok } from '../../types/result/types.ts' import { ok, error } from '../../types/result/module.f.mjs' import { nonEmpty, empty as elEmpty } from '../../effects/list/module.f.mjs' -import type { IoResult } from '../../effects/node/types.ts' import { tryUtf8 } from '../../text/module.f.mjs' + import { dialect as revisionDialect } from '../../media/revision/module.f.ts' import { buildCache, decodeRevisionBlob, initEvo, evo, emptyCache, syncRevision, diff --git a/fjs/cas/module.f.mjs b/fjs/cas/module.f.mjs index dac866789..819485c99 100644 --- a/fjs/cas/module.f.mjs +++ b/fjs/cas/module.f.mjs @@ -2,16 +2,27 @@ * Content-addressable storage utilities for hashing, addressing, and path parsing. * * @module + * + * @import { Sha2, State as Sha2State } from '../crypto/sha2/types.ts' + * @import { Vec } from '../types/bit_vec/types.ts' + * @import { Effect, Operation } from '../effects/types.ts' + * @import { + * IoResult, + * Now, + * RandomInt, + * ReadBytes, + * Readdir, + * Rm, + * } from '../effects/node/types.ts' + * @import { List } from '../effects/list/types.ts' + * @import { Cas, FileCas, FileCasOperation } from './types.ts' */ -/** @import { Sha2, State as Sha2State } from '../crypto/sha2/types.ts' */ import { sha256 } from '../crypto/sha2/module.f.mjs' import { join, normalize, parse } from '../path/module.f.mjs' -/** @import { Vec } from '../types/bit_vec/types.ts' */ import { empty, length, maxLength, maxLengthBytes, msb, vec } from '../types/bit_vec/module.f.mjs' import { cBase32ToVec, vecToCBase32 } from '../basen/cbase32/module.f.mjs' import { foldStep, forEachStep, history, historyStep, okStep, pure, step } from '../effects/module.f.mjs' -/** @import { Effect, Operation } from '../effects/types.ts' */ import { eff } from '../effects/eff/module.f.mjs' import { access, @@ -27,26 +38,10 @@ import { stat, writeBytes, } from '../effects/node/module.f.mjs' -/** @import { - Access, - CreateExclusive, - IoResult, - Mkdir, - Now, - RandomInt, - ReadBytes, - Readdir, - Rename, - Rm, - Stat, - WriteBytes, -} from '../effects/node/types.ts' */ import { toOption } from '../types/nullable/module.f.mjs' import { error, ok, unwrap } from '../types/result/module.f.mjs' import { splitAt } from '../types/string/module.f.mjs' import { nonEmpty, empty as elEmpty } from '../effects/list/module.f.mjs' -/** @import { List } from '../effects/list/types.ts' */ -/** @import { Cas, FileCas, FileCasOperation } from './types.ts' */ const split2 = splitAt(2) diff --git a/fjs/cas/proof.f.mjs b/fjs/cas/proof.f.mjs index 9b74cf9ff..d19cce297 100644 --- a/fjs/cas/proof.f.mjs +++ b/fjs/cas/proof.f.mjs @@ -1,19 +1,22 @@ -/** @import { Vec } from '../types/bit_vec/types.ts' */ +/** + * @import { Vec } from '../types/bit_vec/types.ts' + * @import { FileCasOperation } from './types.ts' + * @import { Effect } from '../effects/types.ts' + * @import { ReadFile, WriteFile, Rm, Mkdir, IoResult } from '../effects/node/types.ts' + * @import { Ok } from '../types/result/types.ts' + * @import { List } from '../effects/list/types.ts' + */ + import { length, maxLength, msb, vec, vec8 } from '../types/bit_vec/module.f.mjs' import { cBase32ToVec, vecToCBase32 } from '../basen/cbase32/module.f.mjs' import { computeSync, sha256 } from '../crypto/sha2/module.f.mjs' import { fileCas, casAddFile, collectRead, casUpload } from './module.f.mjs' -/** @import { FileCasOperation } from './types.ts' */ import { match, pure, runPure, step } from '../effects/module.f.mjs' -/** @import { Effect } from '../effects/types.ts' */ import { mkdir, writeFile, rm, readFile, access } from '../effects/node/module.f.mjs' -/** @import { ReadFile, WriteFile, Rm, Mkdir, IoResult } from '../effects/node/types.ts' */ -/** @import { Ok } from '../types/result/types.ts' */ import { error, ok } from '../types/result/module.f.mjs' import { emptyState, virtual } from '../effects/node/virtual/module.f.mjs' import { join } from '../path/module.f.mjs' import { nonEmpty, empty } from '../effects/list/module.f.mjs' -/** @import { List } from '../effects/list/types.ts' */ import { assert, assertEq, assertNotNullish } from '../asserts/module.f.mjs' const testDir = './test-cas-cli' diff --git a/fjs/media/json/module.f.ts b/fjs/media/json/module.f.ts index b673e1c9d..a43e94c3e 100644 --- a/fjs/media/json/module.f.ts +++ b/fjs/media/json/module.f.ts @@ -16,7 +16,7 @@ import { next, flat, map } from '../../types/list/module.f.mjs' import { concat } from '../../types/string/module.f.mjs' import { stringToList } from '../../text/utf16/module.f.mjs' import type { Result } from '../../types/result/types.ts' -import { parse as parseTokens } from './parser/module.f.ts' +import { parse as parseTokens } from './parser/module.f.mjs' import { tokenize } from './tokenizer/module.f.mjs' import { at, definedEntries } from '../../types/object/module.f.mjs' import type { Entry as ObjectEntry } from '../../types/object/types.ts' diff --git a/fjs/media/json/parser/module.f.mjs b/fjs/media/json/parser/module.f.mjs new file mode 100644 index 000000000..449e835a0 --- /dev/null +++ b/fjs/media/json/parser/module.f.mjs @@ -0,0 +1,219 @@ +/** + * JSON parser that consumes tokenizer output into JSON values. + * + * @module + * + * @import { Unknown } from '../types.ts' + * @import { Result } from '../../../types/result/types.ts' + * @import { List } from '../../../types/list/types.ts' + * @import { Fold } from '../../../types/function/operator/types.ts' + * @import { JsonToken } from '../tokenizer/types.ts' + * @import { _JsonObject, _JsonArray, _StateParse, _JsonState, _JsonStack } from './types.ts' + */ + +import { error, ok } from '../../../types/result/module.f.mjs' +import { fold, next, toArray, concat } from '../../../types/list/module.f.mjs' +import { setReplace } from '../../../types/ordered_map/module.f.mjs' +import { fromMap } from '../../../types/object/module.f.mjs' +import { assertEq } from '../../../asserts/module.f.mjs' + +/** @type {(obj: _JsonObject) => (key: string) => _JsonObject} */ +const addKeyToObject = + obj => key => ({ kind: 'object', values: obj.values, key: key }) + +/** @type {(obj: _JsonObject) => (value: Unknown) => _JsonObject} */ +const addValueToObject = + obj => value => ({ kind: 'object', values: setReplace(obj.key)(value)(obj.values), key: '' }) + +/** @type {(array: _JsonArray) => (value: Unknown) => _JsonArray} */ +const addToArray = + array => value => ({ kind: 'array', values: concat(array.values)([value]) }) + +/** @type {(state: _StateParse) => (key: string) => _JsonState} */ +const pushKey = state => value => { + if (state.top?.kind === 'object') { return { status: '{k', top: addKeyToObject(state.top)(value), stack: state.stack } } + return { status: 'error', message: 'error' } +} + +/** @type {(state: _StateParse) => (value: Unknown) => _JsonState} */ +const pushValue = state => value => { + if (state.top === null) { return { status: 'result', value: value } } + if (state.top.kind === 'array') { return { status: '[v', top: addToArray(state.top)(value), stack: state.stack } } + return { status: '{v', top: addValueToObject(state.top)(value), stack: state.stack } +} + +/** @type {(state: _StateParse) => _JsonState} */ +const startArray = state => { + const newStack = state.top === null ? null : { first: state.top, tail: state.stack } + return { status: '[', top: { kind: 'array', values: null }, stack: newStack } +} + +/** + * Pops the enclosing container off `stack`. `next` is forced here rather than + * left as a `drop(1)` thunk: the stack is written only by startArray/startObject, + * always as a literal cons, and a lazy pop would leave one unforced thunk per + * closed container — a chain that overflows the stack when it is finally forced. + * + * @type {(stack: _JsonStack) => _StateParse} + */ +const popStack = stack => { + const ne = next(stack) + return ne === null + ? { status: '', top: null, stack: null } + : { status: '', top: ne.first, stack: ne.tail } +} + +/** @type {(state: _StateParse) => _JsonState} */ +const endArray = state => { + const array = state.top !== null ? toArray(state.top.values) : null + const newState = popStack(state.stack) + return pushValue(newState)(array) +} + +/** @type {(state: _StateParse) => _JsonState} */ +const startObject = state => { + const newStack = state.top === null ? null : { first: state.top, tail: state.stack } + return { status: '{', top: { kind: 'object', values: null, key: '' }, stack: newStack } +} + +/** @type {(state: _StateParse) => _JsonState} */ +const endObject = state => { + const obj = state.top?.kind === 'object' ? fromMap(state.top.values) : null + const newState = popStack(state.stack) + return pushValue(newState)(obj) +} + +/** @type {(token: JsonToken) => Unknown} */ +const tokenToValue = token => { + switch (token.kind) { + case 'null': return null + case 'false': return false + case 'true': return true + case 'number': return parseFloat(token.value) + case 'string': return token.value + default: return null + } +} + +/** @type {(token: JsonToken) => boolean} */ +const isValueToken = token => { + switch (token.kind) { + case 'null': + case 'false': + case 'true': + case 'number': + case 'string': return true + default: return false + } +} + +/** @type {(token: JsonToken) => (state: _StateParse) => _JsonState} */ +const parseValueOp = token => state => { + switch (token.kind) { + // A value is required here (top level, after `[`+`,`, or after `:`), + // so `]` is never valid — strict JSON has no trailing commas. + case ']': + return { status: 'error', message: 'unexpected token' } + case '[': return startArray(state) + case '{': return startObject(state) + default: + if (isValueToken(token)) { return pushValue(state)(tokenToValue(token)) } + return { status: 'error', message: 'unexpected token' } + } +} + +/** @type {(token: JsonToken) => (state: _StateParse) => _JsonState} */ +const parseArrayStartOp = token => state => { + if (isValueToken(token)) { return pushValue(state)(tokenToValue(token)) } + if (token.kind === '[') { return startArray(state) } + if (token.kind === ']') { return endArray(state) } + if (token.kind === '{') { return startObject(state) } + return { status: 'error', message: 'unexpected token' } +} + +/** @type {(token: JsonToken) => (state: _StateParse) => _JsonState} */ +const parseArrayValueOp = token => state => { + if (token.kind === ']') { return endArray(state) } + if (token.kind === ',') { return { status: '[,', top: state.top, stack: state.stack } } + return { status: 'error', message: 'unexpected token' } +} + +/** @type {(token: JsonToken) => (state: _StateParse) => _JsonState} */ +const parseObjectStartOp = token => state => { + if (token.kind === 'string') { return pushKey(state)(token.value) } + if (token.kind === '}') { return endObject(state) } + return { status: 'error', message: 'unexpected token' } +} + +/** @type {(token: JsonToken) => (state: _StateParse) => _JsonState} */ +const parseObjectKeyOp = token => state => { + if (token.kind === ':') { return { status: '{:', top: state.top, stack: state.stack } } + return { status: 'error', message: 'unexpected token' } +} + +/** @type {(token: JsonToken) => (state: _StateParse) => _JsonState} */ +const parseObjectNextOp = token => state => { + if (token.kind === '}') { return endObject(state) } + if (token.kind === ',') { return { status: '{,', top: state.top, stack: state.stack } } + return { status: 'error', message: 'unexpected token' } +} + +/** @type {(token: JsonToken) => (state: _StateParse) => _JsonState} */ +const parseObjectCommaOp = token => state => { + // After a `,` a member (string key) is required — `}` here would be a + // trailing comma, which strict JSON rejects. + if (token.kind === 'string') { return pushKey(state)(token.value) } + return { status: 'error', message: 'unexpected token' } +} + +/** @type {Fold} */ +const foldOp = token => state => { + if (token.kind === 'eof') + return state + + switch (state.status) { + case 'result': return { status: 'error', message: 'unexpected token' } + case 'error': return { status: 'error', message: state.message } + case '': return parseValueOp(token)(state) + case '[': return parseArrayStartOp(token)(state) + case '[v': return parseArrayValueOp(token)(state) + case '[,': return parseValueOp(token)(state) + case '{': return parseObjectStartOp(token)(state) + case '{k': return parseObjectKeyOp(token)(state) + case '{:': return parseValueOp(token)(state) + case '{v': return parseObjectNextOp(token)(state) + case '{,': return parseObjectCommaOp(token)(state) + } +} + +/** + * Parses a list of JSON tokens into a JSON-compatible value. + * + * Returns `ok` with the parsed value on success, or `error` with a message + * when the token sequence is invalid or incomplete. + * + * @type {(tokenList: List) => Result} + */ +export const parse = tokenList => { + const state = fold(foldOp)({ status: '', top: null, stack: null })(tokenList) + switch (state.status) { + case 'result': return ok(state.value) + case 'error': return error(state.message) + default: return error('unexpected end') + } +} + +export const proof = { + pushKey: { + // `pushKey` is only ever invoked while `state.top` is an object (the + // state machine's `'{'`/`'{,'` statuses guarantee it), so its + // non-object guard is a defensive branch unreachable through `parse`. + // Call it directly to cover that branch. + nonObjectTop: () => { + /** @type {_StateParse} */ + const state = { status: '[', top: { kind: 'array', values: null }, stack: null } + const result = pushKey(state)('key') + assertEq(result.status, 'error') + }, + }, +} diff --git a/fjs/media/json/parser/module.f.ts b/fjs/media/json/parser/module.f.ts deleted file mode 100644 index 54d97d396..000000000 --- a/fjs/media/json/parser/module.f.ts +++ /dev/null @@ -1,270 +0,0 @@ -/** - * JSON parser that consumes tokenizer output into JSON values. - * - * @module - */ - -import type { Unknown } from '../types.ts' - -import type { Result } from '../../../types/result/types.ts' -import { error, ok } from '../../../types/result/module.f.mjs' -import type { List } from '../../../types/list/types.ts' -import { fold, next, toArray, concat } from '../../../types/list/module.f.mjs' -import type { Fold } from '../../../types/function/operator/types.ts' -import type { JsonToken } from '../tokenizer/types.ts' -import { setReplace } from '../../../types/ordered_map/module.f.mjs' -import type { OrderedMap } from '../../../types/ordered_map/types.ts' -import { fromMap } from '../../../types/object/module.f.mjs' -import { assertEq } from '../../../asserts/module.f.mjs' - -type JsonObject = { - readonly kind: 'object' - readonly values: OrderedMap - readonly key: string -} - -type JsonArray = { - readonly kind: 'array' - readonly values: List -} - -type JsonStackElement = | - JsonObject | - JsonArray - -type JsonStack = List - -type StateParse = { - readonly status: '' | '[' | '[v' | '[,' | '{' | '{k' | '{:' | '{v' | '{,' - readonly top: JsonStackElement | null - readonly stack: JsonStack -} - -type StateResult = { - readonly status: 'result' - readonly value: Unknown -} - -type StateError = { - readonly status: 'error' - readonly message: string -} - -type JsonState = | - StateParse | - StateResult | - StateError - -const addKeyToObject - : (obj: JsonObject) => (key: string) => JsonObject - = obj => key => ({ kind: 'object', values: obj.values, key: key }) - -const addValueToObject - : (obj: JsonObject) => (value: Unknown) => JsonObject - = obj => value => ({ kind: 'object', values: setReplace(obj.key)(value)(obj.values), key: '' }) - -const addToArray - : (array: JsonArray) => (value: Unknown) => JsonArray - = array => value => ({ kind: 'array', values: concat(array.values)([value]) }) - -const pushKey - : (state: StateParse) => (key: string) => JsonState - = state => value => { - if (state.top?.kind === 'object') { return { status: '{k', top: addKeyToObject(state.top)(value), stack: state.stack } } - return { status: 'error', message: 'error' } - } - -const pushValue - : (state: StateParse) => (value: Unknown) => JsonState - = state => value => { - if (state.top === null) { return { status: 'result', value: value } } - if (state.top.kind === 'array') { return { status: '[v', top: addToArray(state.top)(value), stack: state.stack } } - return { status: '{v', top: addValueToObject(state.top)(value), stack: state.stack } - } - -const startArray - : (state: StateParse) => JsonState - = state => { - const newStack = state.top === null ? null : { first: state.top, tail: state.stack } - return { status: '[', top: { kind: 'array', values: null }, stack: newStack } - } - -// Pops the enclosing container off `stack`. `next` is forced here rather than -// left as a `drop(1)` thunk: the stack is written only by startArray/startObject, -// always as a literal cons, and a lazy pop would leave one unforced thunk per -// closed container — a chain that overflows the stack when it is finally forced. -const popStack - : (stack: JsonStack) => StateParse - = stack => { - const ne = next(stack) - return ne === null - ? { status: '', top: null, stack: null } - : { status: '', top: ne.first, stack: ne.tail } - } - -const endArray - : (state: StateParse) => JsonState - = state => { - const array = state.top !== null ? toArray(state.top.values) : null - const newState = popStack(state.stack) - return pushValue(newState)(array) - } - -const startObject - : (state: StateParse) => JsonState - = state => { - const newStack = state.top === null ? null : { first: state.top, tail: state.stack } - return { status: '{', top: { kind: 'object', values: null, key: '' }, stack: newStack } - } - -const endObject - : (state: StateParse) => JsonState - = state => { - const obj = state.top?.kind === 'object' ? fromMap(state.top.values) : null - const newState = popStack(state.stack) - return pushValue(newState)(obj) - } - -const tokenToValue - : (token: JsonToken) => Unknown - = token => { - switch (token.kind) { - case 'null': return null - case 'false': return false - case 'true': return true - case 'number': return parseFloat(token.value) - case 'string': return token.value - default: return null - } - } - -const isValueToken - : (token: JsonToken) => boolean - = token => { - switch (token.kind) { - case 'null': - case 'false': - case 'true': - case 'number': - case 'string': return true - default: return false - } - } - -const parseValueOp - : (token: JsonToken) => (state: StateParse) => JsonState - = token => state => { - switch (token.kind) { - // A value is required here (top level, after `[`+`,`, or after `:`), - // so `]` is never valid — strict JSON has no trailing commas. - case ']': - return { status: 'error', message: 'unexpected token' } - case '[': return startArray(state) - case '{': return startObject(state) - default: - if (isValueToken(token)) { return pushValue(state)(tokenToValue(token)) } - return { status: 'error', message: 'unexpected token' } - } - } - -const parseArrayStartOp - : (token: JsonToken) => (state: StateParse) => JsonState - = token => state => { - if (isValueToken(token)) { return pushValue(state)(tokenToValue(token)) } - if (token.kind === '[') { return startArray(state) } - if (token.kind === ']') { return endArray(state) } - if (token.kind === '{') { return startObject(state) } - return { status: 'error', message: 'unexpected token' } - } - -const parseArrayValueOp - : (token: JsonToken) => (state: StateParse) => JsonState - = token => state => { - if (token.kind === ']') { return endArray(state) } - if (token.kind === ',') { return { status: '[,', top: state.top, stack: state.stack } } - return { status: 'error', message: 'unexpected token' } - } - -const parseObjectStartOp - : (token: JsonToken) => (state: StateParse) => JsonState - = token => state => { - if (token.kind === 'string') { return pushKey(state)(token.value) } - if (token.kind === '}') { return endObject(state) } - return { status: 'error', message: 'unexpected token' } - } - -const parseObjectKeyOp - : (token: JsonToken) => (state: StateParse) => JsonState - = token => state => { - if (token.kind === ':') { return { status: '{:', top: state.top, stack: state.stack } } - return { status: 'error', message: 'unexpected token' } - } - -const parseObjectNextOp - : (token: JsonToken) => (state: StateParse) => JsonState - = token => state => { - if (token.kind === '}') { return endObject(state) } - if (token.kind === ',') { return { status: '{,', top: state.top, stack: state.stack } } - return { status: 'error', message: 'unexpected token' } - } - -const parseObjectCommaOp - : (token: JsonToken) => (state: StateParse) => JsonState - = token => state => { - // After a `,` a member (string key) is required — `}` here would be a - // trailing comma, which strict JSON rejects. - if (token.kind === 'string') { return pushKey(state)(token.value) } - return { status: 'error', message: 'unexpected token' } - } - -const foldOp - : Fold - = token => state => { - if (token.kind === 'eof') - return state - - switch (state.status) { - case 'result': return { status: 'error', message: 'unexpected token' } - case 'error': return { status: 'error', message: state.message } - case '': return parseValueOp(token)(state) - case '[': return parseArrayStartOp(token)(state) - case '[v': return parseArrayValueOp(token)(state) - case '[,': return parseValueOp(token)(state) - case '{': return parseObjectStartOp(token)(state) - case '{k': return parseObjectKeyOp(token)(state) - case '{:': return parseValueOp(token)(state) - case '{v': return parseObjectNextOp(token)(state) - case '{,': return parseObjectCommaOp(token)(state) - } - } - -/** - * Parses a list of JSON tokens into a JSON-compatible value. - * - * Returns `ok` with the parsed value on success, or `error` with a message - * when the token sequence is invalid or incomplete. - */ -export const parse - : (tokenList: List) => Result - = tokenList => { - const state = fold(foldOp)({ status: '', top: null, stack: null })(tokenList) - switch (state.status) { - case 'result': return ok(state.value) - case 'error': return error(state.message) - default: return error('unexpected end') - } - } - -export const proof = { - pushKey: { - // `pushKey` is only ever invoked while `state.top` is an object (the - // state machine's `'{'`/`'{,'` statuses guarantee it), so its - // non-object guard is a defensive branch unreachable through `parse`. - // Call it directly to cover that branch. - nonObjectTop: () => { - const state: StateParse = { status: '[', top: { kind: 'array', values: null }, stack: null } - const result = pushKey(state)('key') - assertEq(result.status, 'error') - }, - }, -} diff --git a/fjs/media/json/parser/proof.f.ts b/fjs/media/json/parser/proof.f.ts index 66387429e..c895c17df 100644 --- a/fjs/media/json/parser/proof.f.ts +++ b/fjs/media/json/parser/proof.f.ts @@ -1,4 +1,4 @@ -import { parse } from './module.f.ts' +import { parse } from './module.f.mjs' import { tokenize } from '../tokenizer/module.f.mjs' import type { JsonToken } from '../tokenizer/types.ts' import { toArray } from '../../../types/list/module.f.mjs' diff --git a/fjs/media/json/parser/types.ts b/fjs/media/json/parser/types.ts new file mode 100644 index 000000000..7bae291f3 --- /dev/null +++ b/fjs/media/json/parser/types.ts @@ -0,0 +1,47 @@ +/** + * Internal state types for the JSON parser. + * + * @module + */ + +import type { Unknown } from '../types.ts' +import type { OrderedMap } from '../../../types/ordered_map/types.ts' +import type { List } from '../../../types/list/types.ts' + +export type _JsonObject = { + readonly kind: 'object' + readonly values: OrderedMap + readonly key: string +} + +export type _JsonArray = { + readonly kind: 'array' + readonly values: List +} + +type _JsonStackElement = | + _JsonObject | + _JsonArray + +export type _JsonStack = List<_JsonStackElement> + +export type _StateParse = { + readonly status: '' | '[' | '[v' | '[,' | '{' | '{k' | '{:' | '{v' | '{,' + readonly top: _JsonStackElement | null + readonly stack: _JsonStack +} + +type _StateResult = { + readonly status: 'result' + readonly value: Unknown +} + +type _StateError = { + readonly status: 'error' + readonly message: string +} + +export type _JsonState = | + _StateParse | + _StateResult | + _StateError