mirror of
https://github.com/voson-wang/toon.git
synced 2026-01-29 23:34:10 +08:00
feat(decoder): indentation strict-mode enforcement
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import type { Depth, ParsedLine } from '../types'
|
||||
import { SPACE } from '../constants'
|
||||
import { SPACE, TAB } from '../constants'
|
||||
|
||||
export class LineCursor {
|
||||
private lines: ParsedLine[]
|
||||
@@ -50,7 +50,7 @@ export class LineCursor {
|
||||
}
|
||||
}
|
||||
|
||||
export function toParsedLines(source: string, indentSize: number): ParsedLine[] {
|
||||
export function toParsedLines(source: string, indentSize: number, strict: boolean): ParsedLine[] {
|
||||
if (!source.trim()) {
|
||||
return []
|
||||
}
|
||||
@@ -58,15 +58,41 @@ export function toParsedLines(source: string, indentSize: number): ParsedLine[]
|
||||
const lines = source.split('\n')
|
||||
const parsed: ParsedLine[] = []
|
||||
|
||||
for (const raw of lines) {
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const raw = lines[i]!
|
||||
let indent = 0
|
||||
while (indent < raw.length && raw[indent] === SPACE) {
|
||||
indent++
|
||||
}
|
||||
|
||||
const content = raw.slice(indent)
|
||||
|
||||
// Skip empty lines or lines with only whitespace
|
||||
if (!content.trim()) {
|
||||
continue
|
||||
}
|
||||
|
||||
const depth = computeDepthFromIndent(indent, indentSize)
|
||||
|
||||
// Strict mode validation
|
||||
if (strict) {
|
||||
// Find the full leading whitespace region (spaces and tabs)
|
||||
let wsEnd = 0
|
||||
while (wsEnd < raw.length && (raw[wsEnd] === SPACE || raw[wsEnd] === TAB)) {
|
||||
wsEnd++
|
||||
}
|
||||
|
||||
// Check for tabs in leading whitespace (before actual content)
|
||||
if (raw.slice(0, wsEnd).includes(TAB)) {
|
||||
throw new SyntaxError(`Line ${i + 1}: Tabs are not allowed in indentation in strict mode`)
|
||||
}
|
||||
|
||||
// Check for exact multiples of indentSize
|
||||
if (indent > 0 && indent % indentSize !== 0) {
|
||||
throw new SyntaxError(`Line ${i + 1}: Indentation must be exact multiple of ${indentSize}, but found ${indent} spaces`)
|
||||
}
|
||||
}
|
||||
|
||||
parsed.push({ raw, indent, content, depth })
|
||||
}
|
||||
|
||||
|
||||
@@ -26,15 +26,15 @@ export function encode(input: unknown, options?: EncodeOptions): string {
|
||||
}
|
||||
|
||||
export function decode(input: string, options?: DecodeOptions): JsonValue {
|
||||
const resolved = resolveDecodeOptions(options)
|
||||
const lines = toParsedLines(input, resolved.indent)
|
||||
const resolvedOptions = resolveDecodeOptions(options)
|
||||
const lines = toParsedLines(input, resolvedOptions.indent, resolvedOptions.strict)
|
||||
|
||||
if (lines.length === 0) {
|
||||
throw new TypeError('Cannot decode empty input: input must be a non-empty string')
|
||||
}
|
||||
|
||||
const cursor = new LineCursor(lines)
|
||||
return decodeValueFromLines(cursor, resolved)
|
||||
return decodeValueFromLines(cursor, resolvedOptions)
|
||||
}
|
||||
|
||||
function resolveOptions(options?: EncodeOptions): ResolvedEncodeOptions {
|
||||
|
||||
Reference in New Issue
Block a user