define basic Partial CST for JSON

This commit is contained in:
Yura Dupyn 2026-04-25 17:44:05 +02:00
parent ef1d81f597
commit 57f666118a
4 changed files with 121 additions and 4 deletions

18
QESTIONS.md Normal file
View file

@ -0,0 +1,18 @@
# tokens in Partial Concrete Syntax
What sort of tokens should I track in the syntax?
- delimiters like e.g. in `[a, b, c]`?
- more significant separators like the `:` in `{ "foo" : a }`?
- What about groupin symbols like `{ ... }`?
- What about keywords? e.g. `fn` or `for` or `while`?
Can these questions be answered universally, or is this application dependent?
- For example, maybe when building a compiler, we don't need to track so much stuff.
- But for formatter, we probably need to track a bit more.
- But what about something like a library in a IDE that handles various transformations of the code?
# delimiter confusion
I just realized that I've been misunderstanding the word `delimiter`.
I thought that a delimiter was like the `,` in `[ a, b, c]`. But that's called properly called a separator! Or item-separator.
I thought separator and delimiter where synonyms. But it seems like a `delimiter` is actually the grouping symbols like `[` or `]`.

View file

@ -0,0 +1,4 @@
import type { CodePoint, CodePointSpan } from 'source-region';
export type ParseError =
| {} // TODO

View file

@ -0,0 +1,95 @@
import type { CodePointSpan } from 'source-region';
import type { ParseError } from './parse_errors.ts';
export type ConcreteInfo = { span: CodePointSpan };
export type ConcreteError = ConcreteErrorNode[] // Convention: can't be empty.
export type ConcreteErrorNode = {
span: CodePointSpan,
error: ParseError,
panickedOver?: CodePointSpan,
}
export namespace ConcreteError {
export function single(node: ConcreteErrorNode): ConcreteError {
return [node];
}
}
export type DelimiterToken =
| { tag: "open-brace", span: CodePointSpan }
| { tag: "close-brace", span: CodePointSpan }
| { tag: "open-bracket", span: CodePointSpan }
| { tag: "close-bracket", span: CodePointSpan }
export type Program<Info, Error> = {
tag: "program",
expressions: JsonValue<Info, Error>[],
error?: Error,
} & Info
export type JsonValue<Info, Error> =
| JsonObject<Info, Error>
| JsonArray<Info, Error>
| JsonScalar<Info, Error>
| { tag: "error-expression", error: Error } & Info
export type JsonObject<Info, Error> = {
tag: "object",
open: DelimiterToken,
members: MemberItem<Info, Error>[],
close?: DelimiterToken,
error?: Error
} & Info
export type MemberItem<Info, Error> =
| { tag: "member" } & Member<Info, Error>
| { tag: "error-object-separator", error: Error } & Info
export type Member<Info, Error> = {
key: StringLiteral<Info, Error>,
colon?: { tag: "colon", span: CodePointSpan },
value: JsonValue<Info, Error>,
error?: Error
} & Info
export type JsonArray<Info, Error> = {
tag: "array",
open: DelimiterToken,
items: ArrayItem<Info, Error>[],
close?: DelimiterToken,
error?: Error
} & Info
export type ArrayItem<Info, Error> =
| JsonValue<Info, Error>
| { tag: "error-array-separator", error: Error } & Info
export type StringLiteral<Info, Error> =
| {
tag: "string",
// TODO: There are various possibilities of storing the actual literal value. But I don't care about this right now.
value: string,
error?: Error,
} & Info
| { tag: "error-string", error: Error } & Info
export type NumberLiteral<Info, Error> =
| {
tag: "number",
// TODO: There are various possibilities of storing the actual literal value. But I don't care about this right now.
value: number,
error?: Error,
} & Info
| { tag: "error-number", error: Error } & Info
export type JsonScalar<Info, Error> =
// === number ===
| NumberLiteral<Info, Error>
// === string ===
| StringLiteral<Info, Error>
// === constants ===
| { tag: "null", error?: Error } & Info
| { tag: "true", error?: Error } & Info
| { tag: "false", error?: Error } & Info

View file

@ -17,10 +17,10 @@ export namespace ConcreteError {
} }
export type DelimiterToken = export type DelimiterToken =
| { tag: "open-paren"; span: CodePointSpan } | { tag: "open-paren", span: CodePointSpan }
| { tag: "close-paren"; span: CodePointSpan } | { tag: "close-paren", span: CodePointSpan }
| { tag: "open-bracket"; span: CodePointSpan } | { tag: "open-bracket", span: CodePointSpan }
| { tag: "close-bracket"; span: CodePointSpan }; | { tag: "close-bracket", span: CodePointSpan }
export namespace DelimiterToken { export namespace DelimiterToken {
export function openParen(span: CodePointSpan): DelimiterToken { export function openParen(span: CodePointSpan): DelimiterToken {