Skip to content

Commit 94e172e

Browse files
committed
Implemented complete DTD parsing and grammar generation
1 parent ec78574 commit 94e172e

21 files changed

Lines changed: 1122 additions & 118 deletions

AI_AGENT_GUIDELINES.md

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -155,19 +155,47 @@ if (!root) return; // Required check
155155

156156
### Current DTD Support
157157

158-
- Element declarations (`<!ELEMENT>`)
159-
- Attribute list declarations (`<!ATTLIST>`)
160-
- Entity declarations (`<!ENTITY>`)
161-
- Notation declarations (`<!NOTATION>`)
162-
- Internal subsets
163-
- External DTD references
158+
- **Element declarations** (`<!ELEMENT>`) with full content model parsing
159+
- **Attribute list declarations** (`<!ATTLIST>`) with all attribute types and defaults
160+
- **Entity declarations** (`<!ENTITY>`) including parameter entities
161+
- **Notation declarations** (`<!NOTATION>`) with public/system IDs
162+
- **Internal subsets** and **External DTD references**
163+
- **Complete Grammar generation** with content model objects
164+
- **Content model parsing** supporting:
165+
- EMPTY, ANY, Mixed, and Children content types
166+
- Complex sequences, choices, and cardinality operators
167+
- Element children resolution and structural validation
168+
169+
### Advanced Grammar Features
170+
171+
```typescript
172+
// Parse DTD and generate complete Grammar
173+
const dtdParser = new DTDParser();
174+
const grammar = dtdParser.parseDTD('schema.dtd');
175+
176+
// Access content models
177+
const bookModel = grammar.getContentModel('book');
178+
console.log('Content type:', bookModel.getType());
179+
console.log('Child elements:', [...bookModel.getChildren()]);
180+
181+
// Parse individual content models
182+
const model = ContentModel.parse('(title, author+, (chapter | appendix)+)');
183+
console.log('Parsed model:', model.toString());
184+
```
185+
186+
### DTD Processing Capabilities
187+
188+
- **Content Model Parsing**: Complex DTD content specifications
189+
- **Mixed Content Detection**: Automatic identification of #PCDATA mixed content
190+
- **Structural Validation**: Cross-reference checking between element declarations
191+
- **Entity Resolution**: Parameter entity processing and substitution
192+
- **Cardinality Support**: ?, +, * operators on all content particles
164193

165194
### DTD Limitations
166195

167-
- No validation against DTD rules
168-
- Parameter entities supported but limited
169-
- Conditional sections supported
170-
- No default attribute value application
196+
- No runtime validation against DTD rules (parsing only)
197+
- Default attribute values not automatically applied during XML parsing
198+
- Complex parameter entity scenarios may need manual resolution
171199

172200
## Namespace Handling
173201

API_DOCUMENTATION.md

Lines changed: 160 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,23 +440,180 @@ reader.closeFile();
440440

441441
## DTD Support
442442

443-
The library includes comprehensive DTD (Document Type Definition) support:
443+
The library includes comprehensive DTD (Document Type Definition) support with complete Grammar generation:
444444

445-
### DTD Classes
445+
### DTD Parser and Grammar
446446

447447
```typescript
448448
import {
449449
DTDParser,
450+
Grammar,
451+
ContentModel,
450452
ElementDecl,
451453
AttListDecl,
452454
EntityDecl,
453455
NotationDecl,
454456
InternalSubset
455457
} from 'typesxml';
456458

457-
// Parse DTD
459+
// Parse DTD and generate Grammar
458460
const dtdParser = new DTDParser();
459461
const grammar = dtdParser.parseDTD('schema.dtd');
462+
463+
// Access parsed components
464+
const elementDeclarations = grammar.getElementDeclMap();
465+
const attributeDeclarations = grammar.getAttributesMap();
466+
const entities = grammar.getEntitiesMap();
467+
const notations = grammar.getNotationsMap();
468+
```
469+
470+
### Grammar Class
471+
472+
The `Grammar` class represents a complete parsed DTD with all its components:
473+
474+
#### Grammar Methods
475+
476+
| Method | Description | Returns |
477+
|--------|-------------|---------|
478+
| `getContentModel(elementName)` | Get content model for an element | `ContentModel \| undefined` |
479+
| `getElementDeclMap()` | Get all element declarations | `Map<string, ElementDecl>` |
480+
| `getAttributesMap()` | Get all attribute declarations | `Map<string, Map<string, AttDecl>>` |
481+
| `getElementAttributesMap(element)` | Get attributes for specific element | `Map<string, AttDecl> \| undefined` |
482+
| `getEntitiesMap()` | Get all entity declarations | `Map<string, EntityDecl>` |
483+
| `getNotationsMap()` | Get all notation declarations | `Map<string, NotationDecl>` |
484+
| `getEntity(name)` | Get specific entity by name | `EntityDecl \| undefined` |
485+
486+
#### Grammar Usage Example
487+
488+
```typescript
489+
// Parse DTD
490+
const grammar = dtdParser.parseDTD('bookstore.dtd');
491+
492+
// Check element structure
493+
const bookModel = grammar.getContentModel('book');
494+
if (bookModel) {
495+
console.log('Book content type:', bookModel.getType());
496+
console.log('Book children:', [...bookModel.getChildren()]);
497+
}
498+
499+
// Check attributes for an element
500+
const bookAttributes = grammar.getElementAttributesMap('book');
501+
bookAttributes?.forEach((attDecl, attName) => {
502+
console.log(`${attName}: ${attDecl.getType()} = ${attDecl.getDefaultValue()}`);
503+
});
504+
505+
// Access entities
506+
const copyrightEntity = grammar.getEntity('copyright');
507+
if (copyrightEntity) {
508+
console.log('Copyright text:', copyrightEntity.getValue());
509+
}
510+
```
511+
512+
### ContentModel Class
513+
514+
The `ContentModel` class represents the content structure of XML elements:
515+
516+
#### Content Model Types
517+
518+
- **EMPTY**: Element cannot contain any content
519+
- **ANY**: Element can contain any content
520+
- **Mixed**: Element contains #PCDATA mixed with child elements
521+
- **Children**: Element contains only child elements (sequences/choices)
522+
523+
#### ContentModel Instance Methods
524+
525+
| Method | Description | Returns |
526+
|--------|-------------|---------|
527+
| `getType()` | Get content model type | `'EMPTY' \| 'ANY' \| 'Mixed' \| 'Children'` |
528+
| `getContent()` | Get content particles array | `Array<ContentParticle>` |
529+
| `getChildren()` | Get set of child element names | `Set<string>` |
530+
| `isMixed()` | Check if content is mixed | `boolean` |
531+
| `toString()` | Get string representation | `string` |
532+
533+
#### ContentModel Static Methods
534+
535+
| Method | Description | Parameters | Returns |
536+
|--------|-------------|------------|---------|
537+
| `ContentModel.parse(modelString)` | Parse content model from string | `modelString: string` | `ContentModel` |
538+
539+
#### ContentModel Usage Example
540+
541+
```typescript
542+
// Parse content model from DTD declaration
543+
const bookModel = ContentModel.parse('(title, author+, (chapter | appendix)+)');
544+
545+
console.log('Content type:', bookModel.getType()); // 'Children'
546+
console.log('String form:', bookModel.toString()); // '(title,author+,(chapter|appendix)+)'
547+
console.log('Child elements:', [...bookModel.getChildren()]); // ['title', 'author', 'chapter', 'appendix']
548+
549+
// Check for mixed content
550+
const paraModel = ContentModel.parse('(#PCDATA | em | strong)*');
551+
console.log('Is mixed:', paraModel.isMixed()); // true
552+
console.log('Inline elements:', [...paraModel.getChildren()]); // ['em', 'strong']
553+
```
554+
555+
### Content Particles
556+
557+
Content models are composed of particles representing different content types:
558+
559+
#### ContentParticle Types
560+
561+
- **DTDName**: Represents a child element name
562+
- **DTDChoice**: Represents alternatives (A | B | C)
563+
- **DTDSecuence**: Represents sequences (A, B, C)
564+
- **DTDPCData**: Represents #PCDATA content
565+
566+
#### Cardinality Support
567+
568+
All particles support XML cardinality operators:
569+
570+
- **NONE** (default): Exactly one occurrence
571+
- **OPTIONAL** (?): Zero or one occurrence
572+
- **ZEROMANY** (*): Zero or more occurrences
573+
- **ONEMANY** (+): One or more occurrences
574+
575+
### DTD Declarations
576+
577+
#### ElementDecl
578+
579+
```typescript
580+
const elementDecl = grammar.getElementDeclMap().get('book');
581+
console.log('Element name:', elementDecl.getName());
582+
console.log('Content spec:', elementDecl.getContentSpec());
583+
```
584+
585+
#### AttDecl (Attribute Declaration)
586+
587+
```typescript
588+
const bookAttributes = grammar.getElementAttributesMap('book');
589+
const idAttr = bookAttributes?.get('id');
590+
if (idAttr) {
591+
console.log('Attribute name:', idAttr.getName());
592+
console.log('Attribute type:', idAttr.getType()); // 'ID', 'CDATA', etc.
593+
console.log('Default value:', idAttr.getDefaultValue());
594+
console.log('Default declaration:', idAttr.getDefaultDecl()); // '#REQUIRED', '#IMPLIED', etc.
595+
}
596+
```
597+
598+
#### EntityDecl
599+
600+
```typescript
601+
const entities = grammar.getEntitiesMap();
602+
entities.forEach((entity, name) => {
603+
console.log(`Entity ${name}:`, entity.getValue());
604+
console.log('Is parameter entity:', entity.isParameterEntity());
605+
});
606+
```
607+
608+
#### NotationDecl
609+
610+
```typescript
611+
const notations = grammar.getNotationsMap();
612+
notations.forEach((notation, name) => {
613+
console.log(`Notation ${name}:`);
614+
console.log('Public ID:', notation.getPublicId());
615+
console.log('System ID:', notation.getSystemId());
616+
});
460617
```
461618

462619
### Catalog Support

API_TYPES.d.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,17 @@ export declare class DTDParser {
241241

242242
export declare class Grammar {
243243
constructor();
244+
getContentModel(elementName: string): ContentModel | undefined;
245+
getElementDeclMap(): Map<string, ElementDecl>;
246+
getAttributesMap(): Map<string, Map<string, AttDecl>>;
247+
getElementAttributesMap(element: string): Map<string, AttDecl> | undefined;
248+
getEntitiesMap(): Map<string, EntityDecl>;
249+
getNotationsMap(): Map<string, NotationDecl>;
250+
getEntity(entityName: string): EntityDecl | undefined;
251+
addElement(elementDecl: ElementDecl): void;
252+
addEntity(entityDecl: EntityDecl): void;
253+
addNotation(notation: NotationDecl): void;
254+
merge(grammar: Grammar): void;
244255
}
245256

246257
export declare class ElementDecl implements XMLNode {
@@ -271,9 +282,12 @@ export declare class AttDecl implements XMLNode {
271282
}
272283

273284
export declare class EntityDecl implements XMLNode {
274-
constructor(name: string, value: string);
285+
constructor(name: string, parameterEntity: boolean, value: string, systemId: string, publicId: string, ndata: string);
275286
getName(): string;
276287
getValue(): string;
288+
getSystemId(): string;
289+
getPublicId(): string;
290+
isParameterEntity(): boolean;
277291
getNodeType(): number;
278292
toString(): string;
279293
equals(node: XMLNode): boolean;
@@ -298,9 +312,29 @@ export declare class InternalSubset implements XMLNode {
298312
}
299313

300314
export declare class ContentModel {
301-
constructor();
315+
constructor(content: Array<ContentParticle>, type: ContentModelType);
316+
static parse(modelString: string): ContentModel;
317+
getType(): ContentModelType;
318+
getContent(): Array<ContentParticle>;
319+
getChildren(): Set<string>;
320+
isMixed(): boolean;
321+
toString(): string;
302322
}
303323

324+
export interface ContentParticle {
325+
getType(): ContentParticleType;
326+
addParticle(particle: ContentParticle): void;
327+
setCardinality(cardinality: Cardinality): void;
328+
getCardinality(): Cardinality;
329+
getParticles(): Array<ContentParticle>;
330+
getChildren(): Set<string>;
331+
toString(): string;
332+
}
333+
334+
export type ContentModelType = 'EMPTY' | 'ANY' | 'Mixed' | '#PCDATA' | 'Children';
335+
export type ContentParticleType = 0 | 1 | 2 | 3; // PCDATA | NAME | SEQUENCE | CHOICE
336+
export type Cardinality = 0 | 1 | 2 | 3; // NONE | OPTIONAL | ZEROMANY | ONEMANY
337+
304338
// Common Usage Types
305339
export type ParseOptions = {
306340
encoding?: BufferEncoding;

0 commit comments

Comments
 (0)