Papyrus Lint AST cache entry
The JSON Schema for one entry in the on-disk ast-cache used to skip re-parsing unchanged scripts, keyed by the cached script's content hash, modification time, and the linter version that wrote it.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://papyrus-lint.idrinth.de/schema/ast-cache-entry.schema.json",
"title": "Papyrus Lint AST cache entry",
"description": "One `<md5-of-source-path>.json` file inside an `ast-cache` directory next to a running PapyrusLinterCLI/desktop app binary (see app/crates/papyrus-ast-cache/src/lib.rs, re-exported by papyrus-lint-core as its own ast_cache module). This describes entries whose `linter_version` is at or above MIN_COMPATIBLE_VERSION (currently 1.36.0) in that file: an entry is only ever read back by a linter build whose own MIN_COMPATIBLE_VERSION accepts its linter_version, so a consuming tool should apply the same check -- against its own known-compatible floor -- before relying on this schema, and must otherwise treat the entry as opaque and fall back to re-parsing the source itself. MIN_COMPATIBLE_VERSION only moves forward when a release changes this layout or the embedded `ast` shape incompatibly, so a schema fetched at a given linter_version stays valid for every later entry until the next such bump.",
"type": "object",
"additionalProperties": false,
"required": [
"modified_unix_secs",
"content_md5",
"linter_version",
"ast",
"tokens"
],
"properties": {
"modified_unix_secs": {
"description": "The cached source file's last-modified time, in whole seconds since the Unix epoch, at the moment this entry was written. A cache hit requires this to still match the source file's current mtime.",
"type": "integer",
"minimum": 0
},
"content_md5": {
"description": "Lowercase hex MD5 digest of the cached source file's exact byte content at the moment this entry was written. A cache hit requires this to still match a fresh digest of the source's current content.",
"type": "string",
"pattern": "^[0-9a-f]{32}$"
},
"linter_version": {
"description": "The `major.minor.patch` version of the linter release that wrote this entry (Cargo's CARGO_PKG_VERSION). A cache hit requires this to be at or above the reading binary's own MIN_COMPATIBLE_VERSION -- an exact match against the running version is never required, so an ordinary linter update doesn't discard an otherwise still-valid cache.",
"type": "string",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"ast": {
"description": "The parsed script, exactly as produced by papyrus_parser::parse() and serialized via serde -- see app/crates/papyrus-parser/src/ast.rs. Enums with data serialize externally-tagged (e.g. an Expr::Binary value is `{\"Binary\": {\"left\": ..., \"op\": ..., \"right\": ...}}`); enums without data (e.g. BinaryOp, or Expr::Self_) serialize as their bare variant name string. Null when this entry was written by a `put_tokens` call that found no already-cached `ast` to carry forward -- a reader should treat that the same as a miss for the AST specifically and re-parse.",
"anyOf": [
{
"$ref": "#/$defs/script"
},
{
"type": "null"
}
]
},
"tokens": {
"description": "The token stream, exactly as produced by papyrus_parser::tokenize() and serialized via serde -- see app/crates/papyrus-parser/src/token.rs. Null when this entry was written by a `put` call that found no already-cached `tokens` to carry forward -- a reader should treat that the same as a miss for the tokens specifically and re-tokenize. Written alongside `ast` whenever a script is freshly parsed (`parse_psc_file` and `FunctionTable::ensure_loaded`), and read back via `ast_cache::get_tokens`/`ensure_primed`, which primes `papyrus_parser`'s own in-memory memoization on a hit so a subsequent lint/repair pass's internal `tokenize()` call reuses it instead of re-tokenizing.",
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/$defs/token"
}
},
{
"type": "null"
}
]
}
},
"$defs": {
"script": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"extends",
"is_hidden",
"is_conditional",
"imports",
"properties",
"variables",
"functions",
"states",
"line"
],
"properties": {
"name": {
"type": "string"
},
"extends": {
"type": [
"string",
"null"
]
},
"is_hidden": {
"type": "boolean"
},
"is_conditional": {
"type": "boolean"
},
"imports": {
"type": "array",
"items": {
"$ref": "#/$defs/importDecl"
}
},
"properties": {
"type": "array",
"items": {
"$ref": "#/$defs/propertyDecl"
}
},
"variables": {
"type": "array",
"items": {
"$ref": "#/$defs/variableDecl"
}
},
"functions": {
"type": "array",
"items": {
"$ref": "#/$defs/functionDecl"
}
},
"states": {
"type": "array",
"items": {
"$ref": "#/$defs/stateDecl"
}
},
"line": {
"description": "The 1-indexed line the ScriptName keyword itself starts on.",
"type": "integer",
"minimum": 1
}
}
},
"importDecl": {
"description": "A single Import <ScriptName> statement.",
"type": "object",
"additionalProperties": false,
"required": [
"name",
"line"
],
"properties": {
"name": {
"type": "string"
},
"line": {
"description": "The 1-indexed line the Import keyword itself starts on.",
"type": "integer",
"minimum": 1
}
}
},
"typeName": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"is_array"
],
"properties": {
"name": {
"type": "string"
},
"is_array": {
"type": "boolean"
}
}
},
"token": {
"description": "One lexed token, exactly as produced by papyrus_parser::tokenize() -- see app/crates/papyrus-parser/src/token.rs.",
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"line",
"col"
],
"properties": {
"kind": {
"$ref": "#/$defs/tokenKind"
},
"line": {
"type": "integer",
"minimum": 1
},
"col": {
"type": "integer",
"minimum": 1
}
}
},
"keyword": {
"description": "A recognized Papyrus keyword; serializes as its bare variant name string.",
"type": "string",
"enum": [
"ScriptName",
"Extends",
"Hidden",
"Conditional",
"Import",
"Function",
"EndFunction",
"Event",
"EndEvent",
"Property",
"EndProperty",
"Auto",
"AutoReadOnly",
"Global",
"Native",
"Return",
"If",
"ElseIf",
"Else",
"EndIf",
"While",
"EndWhile",
"State",
"EndState",
"New",
"As",
"True",
"False",
"None",
"Self_",
"Parent",
"Length",
"DebugOnly",
"BetaOnly"
]
},
"tokenKind": {
"description": "A token's kind, externally tagged by its variant name; a variant with no data (e.g. LParen, Newline, Eof) serializes as its bare variant name string.",
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"required": [
"Identifier"
],
"properties": {
"Identifier": {
"type": "string"
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Keyword"
],
"properties": {
"Keyword": {
"$ref": "#/$defs/keyword"
}
}
},
{
"description": "The literal's parsed value and the notation (decimal/hexadecimal) it was written with, in that order.",
"type": "object",
"additionalProperties": false,
"required": [
"IntLiteral"
],
"properties": {
"IntLiteral": {
"type": "array",
"prefixItems": [
{
"type": "integer"
},
{
"$ref": "#/$defs/intFormat"
}
],
"items": false,
"minItems": 2,
"maxItems": 2
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"FloatLiteral"
],
"properties": {
"FloatLiteral": {
"type": "number"
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"StringLiteral"
],
"properties": {
"StringLiteral": {
"type": "string"
}
}
},
{
"const": "LParen"
},
{
"const": "RParen"
},
{
"const": "LBracket"
},
{
"const": "RBracket"
},
{
"const": "Comma"
},
{
"const": "Dot"
},
{
"const": "Colon"
},
{
"const": "Assign"
},
{
"const": "PlusAssign"
},
{
"const": "MinusAssign"
},
{
"const": "StarAssign"
},
{
"const": "SlashAssign"
},
{
"const": "PercentAssign"
},
{
"const": "Plus"
},
{
"const": "Minus"
},
{
"const": "Star"
},
{
"const": "Slash"
},
{
"const": "Percent"
},
{
"const": "Eq"
},
{
"const": "NotEq"
},
{
"const": "Gt"
},
{
"const": "Lt"
},
{
"const": "GtEq"
},
{
"const": "LtEq"
},
{
"const": "AndAnd"
},
{
"const": "OrOr"
},
{
"const": "Not"
},
{
"const": "Newline"
},
{
"const": "Eof"
}
]
},
"propertyDecl": {
"type": "object",
"additionalProperties": false,
"required": [
"type_name",
"name",
"value",
"is_auto",
"is_auto_read_only",
"is_hidden",
"is_conditional",
"line"
],
"properties": {
"type_name": {
"$ref": "#/$defs/typeName"
},
"name": {
"type": "string"
},
"value": {
"anyOf": [
{
"$ref": "#/$defs/expr"
},
{
"type": "null"
}
]
},
"is_auto": {
"type": "boolean"
},
"is_auto_read_only": {
"type": "boolean"
},
"is_hidden": {
"type": "boolean"
},
"is_conditional": {
"type": "boolean"
},
"line": {
"type": "integer",
"minimum": 1
}
}
},
"variableDecl": {
"type": "object",
"additionalProperties": false,
"required": [
"type_name",
"name",
"value",
"is_conditional",
"line"
],
"properties": {
"type_name": {
"$ref": "#/$defs/typeName"
},
"name": {
"type": "string"
},
"value": {
"anyOf": [
{
"$ref": "#/$defs/expr"
},
{
"type": "null"
}
]
},
"is_conditional": {
"type": "boolean"
},
"line": {
"type": "integer",
"minimum": 1
}
}
},
"param": {
"type": "object",
"additionalProperties": false,
"required": [
"type_name",
"name",
"default"
],
"properties": {
"type_name": {
"$ref": "#/$defs/typeName"
},
"name": {
"type": "string"
},
"default": {
"anyOf": [
{
"$ref": "#/$defs/expr"
},
{
"type": "null"
}
]
}
}
},
"functionDecl": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"return_type",
"params",
"is_global",
"is_native",
"is_event",
"body",
"line",
"state"
],
"properties": {
"name": {
"type": "string"
},
"return_type": {
"anyOf": [
{
"$ref": "#/$defs/typeName"
},
{
"type": "null"
}
]
},
"params": {
"type": "array",
"items": {
"$ref": "#/$defs/param"
}
},
"is_global": {
"type": "boolean"
},
"is_native": {
"type": "boolean"
},
"is_event": {
"type": "boolean"
},
"body": {
"type": "array",
"items": {
"$ref": "#/$defs/stmt"
}
},
"line": {
"type": "integer",
"minimum": 1
},
"state": {
"description": "The name of the State block this function/event is declared in, or null when it's declared directly on the script (the \"empty state\").",
"type": [
"string",
"null"
]
}
}
},
"stateDecl": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"is_auto",
"functions",
"line"
],
"properties": {
"name": {
"type": "string"
},
"is_auto": {
"type": "boolean"
},
"functions": {
"type": "array",
"items": {
"$ref": "#/$defs/functionDecl"
}
},
"line": {
"type": "integer",
"minimum": 1
}
}
},
"ifBranch": {
"type": "object",
"additionalProperties": false,
"required": [
"condition",
"body",
"line",
"col"
],
"properties": {
"condition": {
"$ref": "#/$defs/expr"
},
"body": {
"type": "array",
"items": {
"$ref": "#/$defs/stmt"
}
},
"line": {
"type": "integer",
"minimum": 1
},
"col": {
"type": "integer",
"minimum": 1
}
}
},
"stmt": {
"description": "A statement, externally tagged by its variant name.",
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"required": [
"VarDecl"
],
"properties": {
"VarDecl": {
"$ref": "#/$defs/variableDecl"
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Assign"
],
"properties": {
"Assign": {
"type": "object",
"additionalProperties": false,
"required": [
"target",
"op",
"value",
"line"
],
"properties": {
"target": {
"$ref": "#/$defs/expr"
},
"op": {
"$ref": "#/$defs/assignOp"
},
"value": {
"$ref": "#/$defs/expr"
},
"line": {
"type": "integer",
"minimum": 1
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Expr"
],
"properties": {
"Expr": {
"type": "object",
"additionalProperties": false,
"required": [
"value",
"line"
],
"properties": {
"value": {
"$ref": "#/$defs/expr"
},
"line": {
"type": "integer",
"minimum": 1
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Return"
],
"properties": {
"Return": {
"type": "object",
"additionalProperties": false,
"required": [
"value",
"line"
],
"properties": {
"value": {
"anyOf": [
{
"$ref": "#/$defs/expr"
},
{
"type": "null"
}
]
},
"line": {
"type": "integer",
"minimum": 1
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"If"
],
"properties": {
"If": {
"type": "object",
"additionalProperties": false,
"required": [
"branches",
"else_body",
"else_line",
"else_col",
"line"
],
"properties": {
"branches": {
"type": "array",
"items": {
"$ref": "#/$defs/ifBranch"
}
},
"else_body": {
"type": "array",
"items": {
"$ref": "#/$defs/stmt"
}
},
"else_line": {
"description": "The line the Else keyword itself starts on, or null when this If has no Else clause at all -- distinguishing that from an empty Else clause, which both leave else_body empty.",
"type": [
"integer",
"null"
],
"minimum": 1
},
"else_col": {
"type": [
"integer",
"null"
],
"minimum": 1
},
"line": {
"type": "integer",
"minimum": 1
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"While"
],
"properties": {
"While": {
"type": "object",
"additionalProperties": false,
"required": [
"condition",
"body",
"line",
"col"
],
"properties": {
"condition": {
"$ref": "#/$defs/expr"
},
"body": {
"type": "array",
"items": {
"$ref": "#/$defs/stmt"
}
},
"line": {
"type": "integer",
"minimum": 1
},
"col": {
"type": "integer",
"minimum": 1
}
}
}
}
}
]
},
"assignOp": {
"type": "string",
"enum": [
"Assign",
"AddAssign",
"SubAssign",
"MulAssign",
"DivAssign",
"ModAssign"
]
},
"binaryOp": {
"type": "string",
"enum": [
"Add",
"Sub",
"Mul",
"Div",
"Mod",
"Eq",
"NotEq",
"Gt",
"Lt",
"GtEq",
"LtEq",
"And",
"Or"
]
},
"unaryOp": {
"type": "string",
"enum": [
"Neg",
"Not"
]
},
"intFormat": {
"description": "The notation an integer literal was written with in source: plain decimal digits, or a 0x/0X-prefixed hexadecimal sequence.",
"type": "string",
"enum": [
"Decimal",
"Hexadecimal"
]
},
"literal": {
"description": "A literal value, externally tagged by its variant name; None (the Papyrus null literal) has no data and serializes as the bare string \"None\".",
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"required": [
"Int"
],
"properties": {
"Int": {
"type": "object",
"additionalProperties": false,
"required": [
"value",
"format"
],
"properties": {
"value": {
"type": "integer"
},
"format": {
"$ref": "#/$defs/intFormat"
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Float"
],
"properties": {
"Float": {
"type": "number"
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"String"
],
"properties": {
"String": {
"type": "string"
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Bool"
],
"properties": {
"Bool": {
"type": "boolean"
}
}
},
{
"const": "None"
}
]
},
"expr": {
"description": "An expression, externally tagged by its variant name; Self_ and Parent have no data and serialize as their bare variant name string.",
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"required": [
"Literal"
],
"properties": {
"Literal": {
"$ref": "#/$defs/literal"
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Identifier"
],
"properties": {
"Identifier": {
"type": "string"
}
}
},
{
"const": "Self_"
},
{
"const": "Parent"
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Binary"
],
"properties": {
"Binary": {
"type": "object",
"additionalProperties": false,
"required": [
"left",
"op",
"right"
],
"properties": {
"left": {
"$ref": "#/$defs/expr"
},
"op": {
"$ref": "#/$defs/binaryOp"
},
"right": {
"$ref": "#/$defs/expr"
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Unary"
],
"properties": {
"Unary": {
"type": "object",
"additionalProperties": false,
"required": [
"op",
"operand"
],
"properties": {
"op": {
"$ref": "#/$defs/unaryOp"
},
"operand": {
"$ref": "#/$defs/expr"
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Call"
],
"properties": {
"Call": {
"type": "object",
"additionalProperties": false,
"required": [
"callee",
"args",
"line",
"col"
],
"properties": {
"callee": {
"$ref": "#/$defs/expr"
},
"args": {
"type": "array",
"items": {
"$ref": "#/$defs/expr"
}
},
"line": {
"type": "integer",
"minimum": 1
},
"col": {
"type": "integer",
"minimum": 1
}
}
}
}
},
{
"description": "A named argument in a call's argument list (func(argB = 1)); only ever appears as an element of Call.args.",
"type": "object",
"additionalProperties": false,
"required": [
"NamedArg"
],
"properties": {
"NamedArg": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"value"
],
"properties": {
"name": {
"type": "string"
},
"value": {
"$ref": "#/$defs/expr"
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Member"
],
"properties": {
"Member": {
"type": "object",
"additionalProperties": false,
"required": [
"object",
"property"
],
"properties": {
"object": {
"$ref": "#/$defs/expr"
},
"property": {
"type": "string"
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Index"
],
"properties": {
"Index": {
"type": "object",
"additionalProperties": false,
"required": [
"object",
"index"
],
"properties": {
"object": {
"$ref": "#/$defs/expr"
},
"index": {
"$ref": "#/$defs/expr"
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"Cast"
],
"properties": {
"Cast": {
"type": "object",
"additionalProperties": false,
"required": [
"value",
"type_name"
],
"properties": {
"value": {
"$ref": "#/$defs/expr"
},
"type_name": {
"type": "string"
}
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"NewArray"
],
"properties": {
"NewArray": {
"type": "object",
"additionalProperties": false,
"required": [
"type_name",
"size"
],
"properties": {
"type_name": {
"$ref": "#/$defs/typeName"
},
"size": {
"$ref": "#/$defs/expr"
}
}
}
}
}
]
}
}
}