@taml/parser
Version:
Parser for TAML (Terminal ANSI Markup Language) that generates AST nodes
251 lines • 17.5 kB
JavaScript
/**
* Validator for TAML tokens and syntax
*/
import { isValidTag } from "@taml/ast";
import { InvalidTagError, MismatchedTagError, UnclosedTagError, } from "./errors.js";
import { isCloseTagToken, isEofToken, isOpenTagToken, isTextToken, } from "./tokenizer.js";
/**
* TAML Validator class
*/
export class TamlValidator {
source;
constructor(source) {
this.source = source;
}
/**
* Validate a sequence of tokens
*/
validateTokens(tokens) {
const context = {
tokens,
position: 0,
source: this.source,
tagStack: [],
errors: [],
};
try {
this.validateTokenSequence(context);
// Check for unclosed tags at the end
if (context.tagStack.length > 0) {
const unclosedTag = context.tagStack[context.tagStack.length - 1];
if (unclosedTag) {
const error = new UnclosedTagError(unclosedTag.tagName, unclosedTag.token.start, unclosedTag.token.line, unclosedTag.token.column, this.source);
context.errors.push(error);
}
}
return {
valid: context.errors.length === 0,
errors: context.errors,
};
}
catch (error) {
if (error instanceof Error) {
context.errors.push(error);
}
return {
valid: false,
errors: context.errors,
};
}
}
/**
* Validate the sequence of tokens
*/
validateTokenSequence(context) {
while (context.position < context.tokens.length) {
const token = context.tokens[context.position];
if (!token) {
break;
}
if (isEofToken(token)) {
break;
}
if (isOpenTagToken(token)) {
this.validateOpenTag(context, token);
}
else if (isCloseTagToken(token)) {
this.validateCloseTag(context, token);
}
else if (isTextToken(token)) {
// Text tokens are always valid
context.position++;
}
else {
// Skip unexpected tokens
context.position++;
}
}
}
/**
* Validate an opening tag token
*/
validateOpenTag(context, token) {
if (!isOpenTagToken(token)) {
context.position++;
return;
}
// Validate tag name against TAML specification
if (!isValidTag(token.tagName)) {
const error = new InvalidTagError(token.tagName, token.start, token.line, token.column, this.source);
context.errors.push(error);
context.position++;
return;
}
// Push tag onto stack for tracking
context.tagStack.push({
tagName: token.tagName,
token,
});
context.position++;
}
/**
* Validate a closing tag token
*/
validateCloseTag(context, token) {
if (!isCloseTagToken(token)) {
context.position++;
return;
}
// Validate tag name against TAML specification
if (!isValidTag(token.tagName)) {
const error = new InvalidTagError(token.tagName, token.start, token.line, token.column, this.source);
context.errors.push(error);
context.position++;
return;
}
// Check if there's a matching opening tag
if (context.tagStack.length === 0) {
// No opening tag to match - this is an extra closing tag
const error = new MismatchedTagError("(none)", token.tagName, token.start, token.line, token.column, this.source);
context.errors.push(error);
context.position++;
return;
}
// Check if the closing tag matches the most recent opening tag
const lastOpenTag = context.tagStack[context.tagStack.length - 1];
if (lastOpenTag && lastOpenTag.tagName !== token.tagName) {
const error = new MismatchedTagError(lastOpenTag.tagName, token.tagName, token.start, token.line, token.column, this.source);
context.errors.push(error);
context.position++;
return;
}
// Valid closing tag - pop from stack
context.tagStack.pop();
context.position++;
}
/**
* Get current validation state for debugging
*/
getDebugInfo(context) {
return {
position: context.position,
currentToken: context.position < context.tokens.length
? (context.tokens[context.position] ?? null)
: null,
tagStack: context.tagStack.map((entry) => entry.tagName),
errorCount: context.errors.length,
};
}
}
/**
* Convenience function to validate TAML tokens
*/
export function validateTamlTokens(tokens, source) {
const validator = new TamlValidator(source);
return validator.validateTokens(tokens);
}
/**
* Validate tag name against TAML specification
*/
export function validateTagName(tagName) {
return isValidTag(tagName);
}
/**
* Check if nesting is valid (stack-based validation)
*/
export function validateNesting(tokens) {
const tagStack = [];
const unclosedTags = [];
const mismatchedTags = [];
for (const token of tokens) {
if (isOpenTagToken(token)) {
if (isValidTag(token.tagName)) {
tagStack.push(token.tagName);
}
}
else if (isCloseTagToken(token)) {
if (isValidTag(token.tagName)) {
if (tagStack.length === 0) {
mismatchedTags.push({ expected: "(none)", actual: token.tagName });
}
else {
const lastTag = tagStack[tagStack.length - 1];
if (lastTag && lastTag === token.tagName) {
tagStack.pop();
}
else if (lastTag) {
mismatchedTags.push({ expected: lastTag, actual: token.tagName });
}
}
}
}
}
// Any remaining tags in stack are unclosed
unclosedTags.push(...tagStack);
return {
valid: unclosedTags.length === 0 && mismatchedTags.length === 0,
unclosedTags,
mismatchedTags,
};
}
/**
* Validate proper tag closure
*/
export function validateTagClosure(tokens) {
const tagStack = [];
const issues = [];
for (const token of tokens) {
if (isOpenTagToken(token)) {
if (isValidTag(token.tagName)) {
tagStack.push({ tagName: token.tagName, position: token.start });
}
}
else if (isCloseTagToken(token)) {
if (isValidTag(token.tagName)) {
if (tagStack.length === 0) {
issues.push({
type: "extra",
tagName: token.tagName,
position: token.start,
});
}
else {
const lastTag = tagStack[tagStack.length - 1];
if (lastTag && lastTag.tagName === token.tagName) {
tagStack.pop();
}
else {
issues.push({
type: "mismatched",
tagName: token.tagName,
position: token.start,
});
}
}
}
}
}
// Any remaining tags in stack are unclosed
for (const unclosedTag of tagStack) {
issues.push({
type: "unclosed",
tagName: unclosedTag.tagName,
position: unclosedTag.position,
});
}
return {
valid: issues.length === 0,
issues,
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvdmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBRUgsT0FBTyxFQUFnQixVQUFVLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDckQsT0FBTyxFQUNMLGVBQWUsRUFDZixrQkFBa0IsRUFDbEIsZ0JBQWdCLEdBQ2pCLE1BQU0sYUFBYSxDQUFDO0FBQ3JCLE9BQU8sRUFFTCxlQUFlLEVBQ2YsVUFBVSxFQUNWLGNBQWMsRUFDZCxXQUFXLEdBQ1osTUFBTSxnQkFBZ0IsQ0FBQztBQXFCeEI7O0dBRUc7QUFDSCxNQUFNLE9BQU8sYUFBYTtJQUNLO0lBQTdCLFlBQTZCLE1BQWM7UUFBZCxXQUFNLEdBQU4sTUFBTSxDQUFRO0lBQUcsQ0FBQztJQUUvQzs7T0FFRztJQUNILGNBQWMsQ0FBQyxNQUFtQjtRQUNoQyxNQUFNLE9BQU8sR0FBc0I7WUFDakMsTUFBTTtZQUNOLFFBQVEsRUFBRSxDQUFDO1lBQ1gsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNO1lBQ25CLFFBQVEsRUFBRSxFQUFFO1lBQ1osTUFBTSxFQUFFLEVBQUU7U0FDWCxDQUFDO1FBRUYsSUFBSSxDQUFDO1lBQ0gsSUFBSSxDQUFDLHFCQUFxQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBRXBDLHFDQUFxQztZQUNyQyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUNoQyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNsRSxJQUFJLFdBQVcsRUFBRSxDQUFDO29CQUNoQixNQUFNLEtBQUssR0FBRyxJQUFJLGdCQUFnQixDQUNoQyxXQUFXLENBQUMsT0FBTyxFQUNuQixXQUFXLENBQUMsS0FBSyxDQUFDLEtBQUssRUFDdkIsV0FBVyxDQUFDLEtB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