Add Shakespeare esolang
This commit is contained in:
74
languages/shakespeare/parser/constants.ts
Normal file
74
languages/shakespeare/parser/constants.ts
Normal file
@ -0,0 +1,74 @@
|
||||
/** Regex bit to accommodate line breaks in multi-word keywords */
|
||||
const br = /(?: |\s*\n\s*)/.source;
|
||||
|
||||
/** Utility to create regex from list of words */
|
||||
const makeRegex = (words: string[], flags?: string) => {
|
||||
const fixedWords = words.map((w) => w.replace(/ /g, br));
|
||||
const pattern = "(" + fixedWords.join("|") + ")\\b";
|
||||
return new RegExp(pattern, flags);
|
||||
};
|
||||
|
||||
// prettier-ignore
|
||||
const CHARACTERS = ["Achilles", "Adonis", "Adriana", "Aegeon", "Aemilia", "Agamemnon", "Agrippa", "Ajax", "Alonso", "Andromache",
|
||||
"Angelo", "Antiochus", "Antonio", "Arthur", "Autolycus", "Balthazar", "Banquo", "Beatrice", "Benedick", "Benvolio", "Bianca",
|
||||
"Brabantio", "Brutus", "Capulet", "Cassandra", "Cassius", "Christopher Sly", "Cicero", "Claudio", "Claudius", "Cleopatra",
|
||||
"Cordelia", "Cornelius", "Cressida", "Cymberline", "Demetrius", "Desdemona", "Dionyza", "Doctor Caius", "Dogberry", "Don John",
|
||||
"Don Pedro", "Donalbain", "Dorcas", "Duncan", "Egeus", "Emilia", "Escalus", "Falstaff", "Fenton", "Ferdinand", "Ford", "Fortinbras",
|
||||
"Francisca", "Friar John", "Friar Laurence", "Gertrude", "Goneril", "Hamlet", "Hecate", "Hector", "Helen", "Helena", "Hermia",
|
||||
"Hermonie", "Hippolyta", "Horatio", "Imogen", "Isabella", "John of Gaunt", "John of Lancaster", "Julia", "Juliet", "Julius Caesar",
|
||||
"King Henry", "King John", "King Lear", "King Richard", "Lady Capulet", "Lady Macbeth", "Lady Macduff", "Lady Montague", "Lennox",
|
||||
"Leonato", "Luciana", "Lucio", "Lychorida", "Lysander", "Macbeth", "Macduff", "Malcolm", "Mariana", "Mark Antony", "Mercutio",
|
||||
"Miranda", "Mistress Ford", "Mistress Overdone", "Mistress Page", "Montague", "Mopsa", "Oberon", "Octavia", "Octavius Caesar",
|
||||
"Olivia", "Ophelia", "Orlando", "Orsino", "Othello", "Page", "Pantino", "Paris", "Pericles", "Pinch", "Polonius", "Pompeius",
|
||||
"Portia", "Priam", "Prince Henry", "Prospero", "Proteus", "Publius", "Puck", "Queen Elinor", "Regan", "Robin", "Romeo", "Rosalind",
|
||||
"Sebastian", "Shallow", "Shylock", "Slender", "Solinus", "Stephano", "Thaisa", "The Abbot of Westminster", "The Apothecary",
|
||||
"The Archbishop of Canterbury", "The Duke of Milan", "The Duke of Venice", "The Ghost", "Theseus", "Thurio", "Timon", "Titania",
|
||||
"Titus", "Troilus", "Tybalt", "Ulysses", "Valentine", "Venus", "Vincentio", "Viola"]
|
||||
|
||||
/** Regex that matches an identified character name */
|
||||
export const CharacterRegex = makeRegex(CHARACTERS, "i");
|
||||
|
||||
// prettier-ignore
|
||||
const NEGATIVE_ADJECTIVE = ["bad","cowardly","cursed","damned","dirty","disgusting", "distasteful","dusty","evil","fat-kidneyed",
|
||||
"fat","fatherless","foul","hairy","half-witted", "horrible","horrid","infected","lying","miserable","misused",
|
||||
"oozing","rotten","rotten","smelly","snotty","sorry", "stinking","stuffed","stupid","vile","villainous","worried"]
|
||||
|
||||
/** Regex that matches a negative adjective */
|
||||
export const NegativeAdjectiveRegex = makeRegex(NEGATIVE_ADJECTIVE, "i");
|
||||
|
||||
// prettier-ignore
|
||||
const NEGATIVE_NOUNS = ["Hell","Microsoft","bastard","beggar","blister","codpiece","coward","curse","death","devil","draught",
|
||||
"famine","flirt-gill","goat","hate","hog","hound","leech","lie","pig","plague","starvation","toad","war","wolf"];
|
||||
|
||||
/** Regex that matches a negative noun */
|
||||
export const NegativeNounRegex = makeRegex(NEGATIVE_NOUNS, "i");
|
||||
|
||||
// prettier-ignore
|
||||
const NEUTRAL_ADJECTIVES = ["big","black","blue","bluest","bottomless","furry","green","hard","huge","large","little",
|
||||
"normal","old","purple","red","rural","small","tiny","white","yellow"];
|
||||
|
||||
/** Regex that matches a neutral adjective */
|
||||
export const NeutralAdjectiveRegex = makeRegex(NEUTRAL_ADJECTIVES, "i");
|
||||
|
||||
// prettier-ignore
|
||||
const NEUTRAL_NOUNS = ["animal","aunt","brother","cat","chihuahua","cousin","cow","daughter","door","face","father",
|
||||
"fellow","granddaughter","grandfather","grandmother","grandson","hair","hamster","horse","lamp","lantern","mistletoe",
|
||||
"moon","morning","mother","nephew","niece","nose","purse","road","roman","sister","sky","son","squirrel","stone wall",
|
||||
"thing","town","tree","uncle","wind"]
|
||||
|
||||
/** Regex that matches a neutral noun */
|
||||
export const NeutralNounRegex = makeRegex(NEUTRAL_NOUNS, "i");
|
||||
|
||||
// prettier-ignore
|
||||
const POSITIVE_ADJECTIVES = ["amazing","beautiful","blossoming","bold","brave","charming","clearest","cunning","cute",
|
||||
"delicious","embroidered","fair","fine","gentle","golden","good","handsome","happy","healthy","honest","lovely","loving",
|
||||
"mighty","noble","peaceful","pretty","prompt","proud","reddest","rich","smooth","sunny","sweet","sweetest","trustworthy","warm"]
|
||||
|
||||
/** Regex that matches a positive adjective */
|
||||
export const PositiveAdjectiveRegex = makeRegex(POSITIVE_ADJECTIVES, "i");
|
||||
|
||||
// prettier-ignore
|
||||
const POSITIVE_NOUN = ["Heaven","King","Lord","angel","flower","happiness","joy","plum","summer's day","hero","rose","kingdom","pony"]
|
||||
|
||||
/** Regex that matches a positive noun */
|
||||
export const PositiveNounRegex = makeRegex(POSITIVE_NOUN, "i");
|
671
languages/shakespeare/parser/cst.d.ts
vendored
Normal file
671
languages/shakespeare/parser/cst.d.ts
vendored
Normal file
@ -0,0 +1,671 @@
|
||||
import type { CstNode, ICstVisitor, IToken } from "chevrotain";
|
||||
|
||||
export interface ProgramCstNode extends CstNode {
|
||||
name: "program";
|
||||
children: ProgramCstChildren;
|
||||
}
|
||||
|
||||
export type ProgramCstChildren = {
|
||||
programTitle: ProgramTitleCstNode[];
|
||||
WhitespaceOrNewline?: IToken[];
|
||||
characterIntro?: CharacterIntroCstNode[];
|
||||
actSection?: ActSectionCstNode[];
|
||||
};
|
||||
|
||||
export interface ProgramTitleCstNode extends CstNode {
|
||||
name: "programTitle";
|
||||
children: ProgramTitleCstChildren;
|
||||
}
|
||||
|
||||
export type ProgramTitleCstChildren = {
|
||||
Word?: IToken[];
|
||||
Period: IToken[];
|
||||
};
|
||||
|
||||
export interface CharacterIntroCstNode extends CstNode {
|
||||
name: "characterIntro";
|
||||
children: CharacterIntroCstChildren;
|
||||
}
|
||||
|
||||
export type CharacterIntroCstChildren = {
|
||||
Character: IToken[];
|
||||
Comma: IToken[];
|
||||
Word?: IToken[];
|
||||
SentenceMark: IToken[];
|
||||
};
|
||||
|
||||
export interface ActHeadingCstNode extends CstNode {
|
||||
name: "actHeading";
|
||||
children: ActHeadingCstChildren;
|
||||
}
|
||||
|
||||
export type ActHeadingCstChildren = {
|
||||
Act: IToken[];
|
||||
RomanNumeral: IToken[];
|
||||
Colon: IToken[];
|
||||
Word?: IToken[];
|
||||
SentenceMark: IToken[];
|
||||
};
|
||||
|
||||
export interface SceneHeadingCstNode extends CstNode {
|
||||
name: "sceneHeading";
|
||||
children: SceneHeadingCstChildren;
|
||||
}
|
||||
|
||||
export type SceneHeadingCstChildren = {
|
||||
Scene: IToken[];
|
||||
RomanNumeral: IToken[];
|
||||
Colon: IToken[];
|
||||
Word?: IToken[];
|
||||
SentenceMark: IToken[];
|
||||
};
|
||||
|
||||
export interface EntranceCstNode extends CstNode {
|
||||
name: "entrance";
|
||||
children: EntranceCstChildren;
|
||||
}
|
||||
|
||||
export type EntranceCstChildren = {
|
||||
SquareBracketOpen: IToken[];
|
||||
Enter: IToken[];
|
||||
Character: IToken[];
|
||||
And?: IToken[];
|
||||
SquareBracketClose: IToken[];
|
||||
};
|
||||
|
||||
export interface ExitCstNode extends CstNode {
|
||||
name: "exit";
|
||||
children: ExitCstChildren;
|
||||
}
|
||||
|
||||
export type ExitCstChildren = {
|
||||
SquareBracketOpen: IToken[];
|
||||
Exit: IToken[];
|
||||
Character: IToken[];
|
||||
SquareBracketClose: IToken[];
|
||||
};
|
||||
|
||||
export interface MultiExitCstNode extends CstNode {
|
||||
name: "multiExit";
|
||||
children: MultiExitCstChildren;
|
||||
}
|
||||
|
||||
export type MultiExitCstChildren = {
|
||||
SquareBracketOpen: IToken[];
|
||||
Exeunt: IToken[];
|
||||
Character?: IToken[];
|
||||
And?: IToken[];
|
||||
SquareBracketClose: IToken[];
|
||||
};
|
||||
|
||||
export interface EntryExitClauseCstNode extends CstNode {
|
||||
name: "entryExitClause";
|
||||
children: EntryExitClauseCstChildren;
|
||||
}
|
||||
|
||||
export type EntryExitClauseCstChildren = {
|
||||
entrance?: EntranceCstNode[];
|
||||
exit?: ExitCstNode[];
|
||||
multiExit?: MultiExitCstNode[];
|
||||
};
|
||||
|
||||
export interface ActSectionCstNode extends CstNode {
|
||||
name: "actSection";
|
||||
children: ActSectionCstChildren;
|
||||
}
|
||||
|
||||
export type ActSectionCstChildren = {
|
||||
actHeading: ActHeadingCstNode[];
|
||||
sceneSection: SceneSectionCstNode[];
|
||||
};
|
||||
|
||||
export interface SceneSectionCstNode extends CstNode {
|
||||
name: "sceneSection";
|
||||
children: SceneSectionCstChildren;
|
||||
}
|
||||
|
||||
export type SceneSectionCstChildren = {
|
||||
sceneHeading: SceneHeadingCstNode[];
|
||||
sceneSectionChunk: SceneSectionChunkCstNode[];
|
||||
};
|
||||
|
||||
export interface SceneSectionChunkCstNode extends CstNode {
|
||||
name: "sceneSectionChunk";
|
||||
children: SceneSectionChunkCstChildren;
|
||||
}
|
||||
|
||||
export type SceneSectionChunkCstChildren = {
|
||||
entryExitClause?: EntryExitClauseCstNode[];
|
||||
dialogueSet?: DialogueSetCstNode[];
|
||||
};
|
||||
|
||||
export interface SpeakerClauseCstNode extends CstNode {
|
||||
name: "speakerClause";
|
||||
children: SpeakerClauseCstChildren;
|
||||
}
|
||||
|
||||
export type SpeakerClauseCstChildren = {
|
||||
Character: IToken[];
|
||||
Colon: IToken[];
|
||||
};
|
||||
|
||||
export interface DialogueSetCstNode extends CstNode {
|
||||
name: "dialogueSet";
|
||||
children: DialogueSetCstChildren;
|
||||
}
|
||||
|
||||
export type DialogueSetCstChildren = {
|
||||
speakerClause: SpeakerClauseCstNode[];
|
||||
dialogueLine: DialogueLineCstNode[];
|
||||
};
|
||||
|
||||
export interface DialogueLineCstNode extends CstNode {
|
||||
name: "dialogueLine";
|
||||
children: DialogueLineCstChildren;
|
||||
}
|
||||
|
||||
export type DialogueLineCstChildren = {
|
||||
conditional?: ConditionalCstNode[];
|
||||
nonConditionalDialogueLine?: NonConditionalDialogueLineCstNode[];
|
||||
};
|
||||
|
||||
export interface NonConditionalDialogueLineCstNode extends CstNode {
|
||||
name: "nonConditionalDialogueLine";
|
||||
children: NonConditionalDialogueLineCstChildren;
|
||||
}
|
||||
|
||||
export type NonConditionalDialogueLineCstChildren = {
|
||||
assignment?: AssignmentCstNode[];
|
||||
stdin?: StdinCstNode[];
|
||||
stdout?: StdoutCstNode[];
|
||||
goto?: GotoCstNode[];
|
||||
stackPush?: StackPushCstNode[];
|
||||
stackPop?: StackPopCstNode[];
|
||||
question?: QuestionCstNode[];
|
||||
};
|
||||
|
||||
export interface NounCstNode extends CstNode {
|
||||
name: "noun";
|
||||
children: NounCstChildren;
|
||||
}
|
||||
|
||||
export type NounCstChildren = {
|
||||
NegativeNoun?: IToken[];
|
||||
NeutralNoun?: IToken[];
|
||||
PositiveNoun?: IToken[];
|
||||
};
|
||||
|
||||
export interface AdjectiveCstNode extends CstNode {
|
||||
name: "adjective";
|
||||
children: AdjectiveCstChildren;
|
||||
}
|
||||
|
||||
export type AdjectiveCstChildren = {
|
||||
NegativeAdjective?: IToken[];
|
||||
NeutralAdjective?: IToken[];
|
||||
PositiveAdjective?: IToken[];
|
||||
};
|
||||
|
||||
export interface PossessiveCstNode extends CstNode {
|
||||
name: "possessive";
|
||||
children: PossessiveCstChildren;
|
||||
}
|
||||
|
||||
export type PossessiveCstChildren = {
|
||||
FirstPersonPossessive?: IToken[];
|
||||
SecondPersonPossessive?: IToken[];
|
||||
ThirdPersonPossessive?: IToken[];
|
||||
};
|
||||
|
||||
export interface ReflexiveCstNode extends CstNode {
|
||||
name: "reflexive";
|
||||
children: ReflexiveCstChildren;
|
||||
}
|
||||
|
||||
export type ReflexiveCstChildren = {
|
||||
FirstPersonReflexive?: IToken[];
|
||||
SecondPersonReflexive?: IToken[];
|
||||
};
|
||||
|
||||
export interface ComparativeCstNode extends CstNode {
|
||||
name: "comparative";
|
||||
children: ComparativeCstChildren;
|
||||
}
|
||||
|
||||
export type ComparativeCstChildren = {
|
||||
PositiveComparative?: IToken[];
|
||||
NegativeComparative?: IToken[];
|
||||
};
|
||||
|
||||
export interface AssignmentCstNode extends CstNode {
|
||||
name: "assignment";
|
||||
children: AssignmentCstChildren;
|
||||
}
|
||||
|
||||
export type AssignmentCstChildren = {
|
||||
exclaimAssignment?: ExclaimAssignmentCstNode[];
|
||||
arithAssignment?: ArithAssignmentCstNode[];
|
||||
};
|
||||
|
||||
export interface ExclaimAssignmentCstNode extends CstNode {
|
||||
name: "exclaimAssignment";
|
||||
children: ExclaimAssignmentCstChildren;
|
||||
}
|
||||
|
||||
export type ExclaimAssignmentCstChildren = {
|
||||
SecondPerson: IToken[];
|
||||
unarticulatedVerbalConstant: UnarticulatedVerbalConstantCstNode[];
|
||||
SentenceMark: IToken[];
|
||||
};
|
||||
|
||||
export interface ArithAssignmentCstNode extends CstNode {
|
||||
name: "arithAssignment";
|
||||
children: ArithAssignmentCstChildren;
|
||||
}
|
||||
|
||||
export type ArithAssignmentCstChildren = {
|
||||
SecondPerson: IToken[];
|
||||
Be: IToken[];
|
||||
As?: IToken[];
|
||||
adjective?: AdjectiveCstNode[];
|
||||
expression: ExpressionCstNode[];
|
||||
SentenceMark: IToken[];
|
||||
};
|
||||
|
||||
export interface StdinCstNode extends CstNode {
|
||||
name: "stdin";
|
||||
children: StdinCstChildren;
|
||||
}
|
||||
|
||||
export type StdinCstChildren = {
|
||||
Listen?: IToken[];
|
||||
To?: IToken[];
|
||||
SecondPersonPossessive?: IToken[];
|
||||
Heart?: IToken[];
|
||||
Open?: IToken[];
|
||||
Mind?: IToken[];
|
||||
SentenceMark: IToken[];
|
||||
};
|
||||
|
||||
export interface StdoutCstNode extends CstNode {
|
||||
name: "stdout";
|
||||
children: StdoutCstChildren;
|
||||
}
|
||||
|
||||
export type StdoutCstChildren = {
|
||||
Open?: IToken[];
|
||||
SecondPersonPossessive?: IToken[];
|
||||
Heart?: IToken[];
|
||||
Speak?: IToken[];
|
||||
Mind?: IToken[];
|
||||
SentenceMark: IToken[];
|
||||
};
|
||||
|
||||
export interface GotoCstNode extends CstNode {
|
||||
name: "goto";
|
||||
children: GotoCstChildren;
|
||||
}
|
||||
|
||||
export type GotoCstChildren = {
|
||||
Let?: IToken[];
|
||||
Us?: IToken[];
|
||||
We?: IToken[];
|
||||
Shall?: IToken[];
|
||||
Must?: IToken[];
|
||||
Return?: IToken[];
|
||||
Proceed?: IToken[];
|
||||
To: IToken[];
|
||||
Act?: IToken[];
|
||||
Scene?: IToken[];
|
||||
RomanNumeral: IToken[];
|
||||
SentenceMark: IToken[];
|
||||
};
|
||||
|
||||
export interface StackPushCstNode extends CstNode {
|
||||
name: "stackPush";
|
||||
children: StackPushCstChildren;
|
||||
}
|
||||
|
||||
export type StackPushCstChildren = {
|
||||
Remember: IToken[];
|
||||
expression: ExpressionCstNode[];
|
||||
SentenceMark: IToken[];
|
||||
};
|
||||
|
||||
export interface StackPopCstNode extends CstNode {
|
||||
name: "stackPop";
|
||||
children: StackPopCstChildren;
|
||||
}
|
||||
|
||||
export type StackPopCstChildren = {
|
||||
Recall: IToken[];
|
||||
Word?: IToken[];
|
||||
SentenceMark: IToken[];
|
||||
};
|
||||
|
||||
export interface QuestionCstNode extends CstNode {
|
||||
name: "question";
|
||||
children: QuestionCstChildren;
|
||||
}
|
||||
|
||||
export type QuestionCstChildren = {
|
||||
Be: IToken[];
|
||||
lhs: ExpressionCstNode[];
|
||||
comparator: ComparatorCstNode[];
|
||||
rhs: ExpressionCstNode[];
|
||||
QuestionMark: IToken[];
|
||||
};
|
||||
|
||||
export interface ConditionalCstNode extends CstNode {
|
||||
name: "conditional";
|
||||
children: ConditionalCstChildren;
|
||||
}
|
||||
|
||||
export type ConditionalCstChildren = {
|
||||
If: IToken[];
|
||||
So?: IToken[];
|
||||
Not?: IToken[];
|
||||
Comma: IToken[];
|
||||
nonConditionalDialogueLine: NonConditionalDialogueLineCstNode[];
|
||||
};
|
||||
|
||||
export interface ComparatorCstNode extends CstNode {
|
||||
name: "comparator";
|
||||
children: ComparatorCstChildren;
|
||||
}
|
||||
|
||||
export type ComparatorCstChildren = {
|
||||
Not?: IToken[];
|
||||
_asComparator?: _asComparatorCstNode[];
|
||||
_simpleComparator?: _simpleComparatorCstNode[];
|
||||
_moreLessComparator?: _moreLessComparatorCstNode[];
|
||||
};
|
||||
|
||||
export interface _asComparatorCstNode extends CstNode {
|
||||
name: "_asComparator";
|
||||
children: _asComparatorCstChildren;
|
||||
}
|
||||
|
||||
export type _asComparatorCstChildren = {
|
||||
As: IToken[];
|
||||
adjective: AdjectiveCstNode[];
|
||||
};
|
||||
|
||||
export interface _simpleComparatorCstNode extends CstNode {
|
||||
name: "_simpleComparator";
|
||||
children: _simpleComparatorCstChildren;
|
||||
}
|
||||
|
||||
export type _simpleComparatorCstChildren = {
|
||||
comparative: ComparativeCstNode[];
|
||||
Than: IToken[];
|
||||
};
|
||||
|
||||
export interface _moreLessComparatorCstNode extends CstNode {
|
||||
name: "_moreLessComparator";
|
||||
children: _moreLessComparatorCstChildren;
|
||||
}
|
||||
|
||||
export type _moreLessComparatorCstChildren = {
|
||||
More?: IToken[];
|
||||
Less?: IToken[];
|
||||
adjective: AdjectiveCstNode[];
|
||||
Than: IToken[];
|
||||
};
|
||||
|
||||
export interface ConstantCstNode extends CstNode {
|
||||
name: "constant";
|
||||
children: ConstantCstChildren;
|
||||
}
|
||||
|
||||
export type ConstantCstChildren = {
|
||||
_simpleConstant?: _simpleConstantCstNode[];
|
||||
_verbalConstant?: _verbalConstantCstNode[];
|
||||
};
|
||||
|
||||
export interface _simpleConstantCstNode extends CstNode {
|
||||
name: "_simpleConstant";
|
||||
children: _simpleConstantCstChildren;
|
||||
}
|
||||
|
||||
export type _simpleConstantCstChildren = {
|
||||
Nothing: IToken[];
|
||||
};
|
||||
|
||||
export interface _verbalConstantCstNode extends CstNode {
|
||||
name: "_verbalConstant";
|
||||
children: _verbalConstantCstChildren;
|
||||
}
|
||||
|
||||
export type _verbalConstantCstChildren = {
|
||||
Article?: IToken[];
|
||||
possessive?: PossessiveCstNode[];
|
||||
unarticulatedVerbalConstant: UnarticulatedVerbalConstantCstNode[];
|
||||
};
|
||||
|
||||
export interface UnarticulatedVerbalConstantCstNode extends CstNode {
|
||||
name: "unarticulatedVerbalConstant";
|
||||
children: UnarticulatedVerbalConstantCstChildren;
|
||||
}
|
||||
|
||||
export type UnarticulatedVerbalConstantCstChildren = {
|
||||
adjective?: AdjectiveCstNode[];
|
||||
noun: NounCstNode[];
|
||||
};
|
||||
|
||||
export interface ExpressionCstNode extends CstNode {
|
||||
name: "expression";
|
||||
children: ExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type ExpressionCstChildren = {
|
||||
sumExpression?: SumExpressionCstNode[];
|
||||
differenceExpression?: DifferenceExpressionCstNode[];
|
||||
productExpression?: ProductExpressionCstNode[];
|
||||
quotientExpression?: QuotientExpressionCstNode[];
|
||||
remainderExpression?: RemainderExpressionCstNode[];
|
||||
factorialExpression?: FactorialExpressionCstNode[];
|
||||
squareExpression?: SquareExpressionCstNode[];
|
||||
squareRootExpression?: SquareRootExpressionCstNode[];
|
||||
cubeExpression?: CubeExpressionCstNode[];
|
||||
twiceExpression?: TwiceExpressionCstNode[];
|
||||
atomicExpression?: AtomicExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface AtomicExpressionCstNode extends CstNode {
|
||||
name: "atomicExpression";
|
||||
children: AtomicExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type AtomicExpressionCstChildren = {
|
||||
Character?: IToken[];
|
||||
FirstPerson?: IToken[];
|
||||
SecondPerson?: IToken[];
|
||||
reflexive?: ReflexiveCstNode[];
|
||||
constant?: ConstantCstNode[];
|
||||
};
|
||||
|
||||
export interface SumExpressionCstNode extends CstNode {
|
||||
name: "sumExpression";
|
||||
children: SumExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type SumExpressionCstChildren = {
|
||||
The: IToken[];
|
||||
Sum: IToken[];
|
||||
Of: IToken[];
|
||||
lhs: ExpressionCstNode[];
|
||||
And: IToken[];
|
||||
rhs: ExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface DifferenceExpressionCstNode extends CstNode {
|
||||
name: "differenceExpression";
|
||||
children: DifferenceExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type DifferenceExpressionCstChildren = {
|
||||
The: IToken[];
|
||||
Difference: IToken[];
|
||||
Between: IToken[];
|
||||
lhs: ExpressionCstNode[];
|
||||
And: IToken[];
|
||||
rhs: ExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface ProductExpressionCstNode extends CstNode {
|
||||
name: "productExpression";
|
||||
children: ProductExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type ProductExpressionCstChildren = {
|
||||
The: IToken[];
|
||||
Product: IToken[];
|
||||
Of: IToken[];
|
||||
lhs: ExpressionCstNode[];
|
||||
And: IToken[];
|
||||
rhs: ExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface QuotientExpressionCstNode extends CstNode {
|
||||
name: "quotientExpression";
|
||||
children: QuotientExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type QuotientExpressionCstChildren = {
|
||||
The: IToken[];
|
||||
Quotient: IToken[];
|
||||
Between: IToken[];
|
||||
lhs: ExpressionCstNode[];
|
||||
And: IToken[];
|
||||
rhs: ExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface RemainderExpressionCstNode extends CstNode {
|
||||
name: "remainderExpression";
|
||||
children: RemainderExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type RemainderExpressionCstChildren = {
|
||||
The: IToken[];
|
||||
Remainder: IToken[];
|
||||
Of: IToken[];
|
||||
Quotient: IToken[];
|
||||
Between: IToken[];
|
||||
lhs: ExpressionCstNode[];
|
||||
And: IToken[];
|
||||
rhs: ExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface FactorialExpressionCstNode extends CstNode {
|
||||
name: "factorialExpression";
|
||||
children: FactorialExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type FactorialExpressionCstChildren = {
|
||||
The: IToken[];
|
||||
Factorial: IToken[];
|
||||
Of: IToken[];
|
||||
expression: ExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface SquareExpressionCstNode extends CstNode {
|
||||
name: "squareExpression";
|
||||
children: SquareExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type SquareExpressionCstChildren = {
|
||||
The: IToken[];
|
||||
Square: IToken[];
|
||||
Of: IToken[];
|
||||
expression: ExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface CubeExpressionCstNode extends CstNode {
|
||||
name: "cubeExpression";
|
||||
children: CubeExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type CubeExpressionCstChildren = {
|
||||
The: IToken[];
|
||||
Cube: IToken[];
|
||||
Of: IToken[];
|
||||
expression: ExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface SquareRootExpressionCstNode extends CstNode {
|
||||
name: "squareRootExpression";
|
||||
children: SquareRootExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type SquareRootExpressionCstChildren = {
|
||||
The: IToken[];
|
||||
Square: IToken[];
|
||||
Root: IToken[];
|
||||
Of: IToken[];
|
||||
expression: ExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface TwiceExpressionCstNode extends CstNode {
|
||||
name: "twiceExpression";
|
||||
children: TwiceExpressionCstChildren;
|
||||
}
|
||||
|
||||
export type TwiceExpressionCstChildren = {
|
||||
Twice: IToken[];
|
||||
expression: ExpressionCstNode[];
|
||||
};
|
||||
|
||||
export interface ICstNodeVisitor<IN, OUT> extends ICstVisitor<IN, OUT> {
|
||||
program(children: ProgramCstChildren, param?: IN): OUT;
|
||||
programTitle(children: ProgramTitleCstChildren, param?: IN): OUT;
|
||||
characterIntro(children: CharacterIntroCstChildren, param?: IN): OUT;
|
||||
actHeading(children: ActHeadingCstChildren, param?: IN): OUT;
|
||||
sceneHeading(children: SceneHeadingCstChildren, param?: IN): OUT;
|
||||
entrance(children: EntranceCstChildren, param?: IN): OUT;
|
||||
exit(children: ExitCstChildren, param?: IN): OUT;
|
||||
multiExit(children: MultiExitCstChildren, param?: IN): OUT;
|
||||
entryExitClause(children: EntryExitClauseCstChildren, param?: IN): OUT;
|
||||
actSection(children: ActSectionCstChildren, param?: IN): OUT;
|
||||
sceneSection(children: SceneSectionCstChildren, param?: IN): OUT;
|
||||
sceneSectionChunk(children: SceneSectionChunkCstChildren, param?: IN): OUT;
|
||||
speakerClause(children: SpeakerClauseCstChildren, param?: IN): OUT;
|
||||
dialogueSet(children: DialogueSetCstChildren, param?: IN): OUT;
|
||||
dialogueLine(children: DialogueLineCstChildren, param?: IN): OUT;
|
||||
nonConditionalDialogueLine(children: NonConditionalDialogueLineCstChildren, param?: IN): OUT;
|
||||
noun(children: NounCstChildren, param?: IN): OUT;
|
||||
adjective(children: AdjectiveCstChildren, param?: IN): OUT;
|
||||
possessive(children: PossessiveCstChildren, param?: IN): OUT;
|
||||
reflexive(children: ReflexiveCstChildren, param?: IN): OUT;
|
||||
comparative(children: ComparativeCstChildren, param?: IN): OUT;
|
||||
assignment(children: AssignmentCstChildren, param?: IN): OUT;
|
||||
exclaimAssignment(children: ExclaimAssignmentCstChildren, param?: IN): OUT;
|
||||
arithAssignment(children: ArithAssignmentCstChildren, param?: IN): OUT;
|
||||
stdin(children: StdinCstChildren, param?: IN): OUT;
|
||||
stdout(children: StdoutCstChildren, param?: IN): OUT;
|
||||
goto(children: GotoCstChildren, param?: IN): OUT;
|
||||
stackPush(children: StackPushCstChildren, param?: IN): OUT;
|
||||
stackPop(children: StackPopCstChildren, param?: IN): OUT;
|
||||
question(children: QuestionCstChildren, param?: IN): OUT;
|
||||
conditional(children: ConditionalCstChildren, param?: IN): OUT;
|
||||
comparator(children: ComparatorCstChildren, param?: IN): OUT;
|
||||
_asComparator(children: _asComparatorCstChildren, param?: IN): OUT;
|
||||
_simpleComparator(children: _simpleComparatorCstChildren, param?: IN): OUT;
|
||||
_moreLessComparator(children: _moreLessComparatorCstChildren, param?: IN): OUT;
|
||||
constant(children: ConstantCstChildren, param?: IN): OUT;
|
||||
_simpleConstant(children: _simpleConstantCstChildren, param?: IN): OUT;
|
||||
_verbalConstant(children: _verbalConstantCstChildren, param?: IN): OUT;
|
||||
unarticulatedVerbalConstant(children: UnarticulatedVerbalConstantCstChildren, param?: IN): OUT;
|
||||
expression(children: ExpressionCstChildren, param?: IN): OUT;
|
||||
atomicExpression(children: AtomicExpressionCstChildren, param?: IN): OUT;
|
||||
sumExpression(children: SumExpressionCstChildren, param?: IN): OUT;
|
||||
differenceExpression(children: DifferenceExpressionCstChildren, param?: IN): OUT;
|
||||
productExpression(children: ProductExpressionCstChildren, param?: IN): OUT;
|
||||
quotientExpression(children: QuotientExpressionCstChildren, param?: IN): OUT;
|
||||
remainderExpression(children: RemainderExpressionCstChildren, param?: IN): OUT;
|
||||
factorialExpression(children: FactorialExpressionCstChildren, param?: IN): OUT;
|
||||
squareExpression(children: SquareExpressionCstChildren, param?: IN): OUT;
|
||||
cubeExpression(children: CubeExpressionCstChildren, param?: IN): OUT;
|
||||
squareRootExpression(children: SquareRootExpressionCstChildren, param?: IN): OUT;
|
||||
twiceExpression(children: TwiceExpressionCstChildren, param?: IN): OUT;
|
||||
}
|
18
languages/shakespeare/parser/generate-cst-types.ts
Normal file
18
languages/shakespeare/parser/generate-cst-types.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { writeFileSync } from "fs";
|
||||
import { resolve } from "path";
|
||||
import { generateCstDts } from "chevrotain";
|
||||
import { ShakespeareParser } from "./parser";
|
||||
|
||||
/**
|
||||
* This script generates CST types for the Shakespeare parser.
|
||||
* To run: `yarn ts-node $(pwd)/generate-cst-types.ts` in this directory.
|
||||
*
|
||||
* The `$(pwd)` makes the path absolute. Due to some reason, relative paths with ts-node
|
||||
* aren't working on my side.
|
||||
*/
|
||||
|
||||
const productions = new ShakespeareParser().getGAstProductions();
|
||||
const dtsString = generateCstDts(productions);
|
||||
|
||||
const dtsPath = resolve(__dirname, "./cst.d.ts");
|
||||
writeFileSync(dtsPath, dtsString);
|
54
languages/shakespeare/parser/index.ts
Normal file
54
languages/shakespeare/parser/index.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { CstNode, IToken, Lexer } from "chevrotain";
|
||||
import { DocumentRange } from "../../types";
|
||||
import { ParseError } from "../../worker-errors";
|
||||
import { ShakespeareParser } from "./parser";
|
||||
import { AllTokens } from "./tokens";
|
||||
import { ShakespeareVisitor } from "./visitor";
|
||||
import { Program } from "./visitor-types";
|
||||
|
||||
export class Parser {
|
||||
private readonly _lexer: Lexer = new Lexer(AllTokens);
|
||||
private readonly _parser: ShakespeareParser = new ShakespeareParser();
|
||||
private readonly _visitor: ShakespeareVisitor = new ShakespeareVisitor();
|
||||
|
||||
public parse(text: string): Program {
|
||||
const tokens = this.runLexer(text);
|
||||
const cst = this.runParser(tokens);
|
||||
return this.runVisitor(cst);
|
||||
}
|
||||
|
||||
private runLexer(text: string): IToken[] {
|
||||
const { tokens, errors } = this._lexer.tokenize(text);
|
||||
if (errors.length > 0) {
|
||||
const error = errors[0];
|
||||
throw new ParseError(error.message, {
|
||||
startLine: error.line ? error.line - 1 : 0,
|
||||
startCol: error.column && error.column - 1,
|
||||
endCol: error.column && error.column + error.length - 1,
|
||||
});
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private runParser(tokens: IToken[]): CstNode {
|
||||
this._parser.input = tokens;
|
||||
const parseResult = this._parser.program();
|
||||
if (this._parser.errors.length > 0) {
|
||||
const error = this._parser.errors[0];
|
||||
throw new ParseError(error.message, this.getRange(error.token));
|
||||
}
|
||||
|
||||
return parseResult;
|
||||
}
|
||||
|
||||
private runVisitor(cst: CstNode): Program {
|
||||
return this._visitor.visit(cst);
|
||||
}
|
||||
|
||||
private getRange(token: IToken): DocumentRange {
|
||||
const startLine = (token.startLine || 1) - 1;
|
||||
const startCol = token.startColumn && token.startColumn - 1;
|
||||
const endCol = token.endColumn;
|
||||
return { startLine, startCol, endCol };
|
||||
}
|
||||
}
|
589
languages/shakespeare/parser/parser.ts
Normal file
589
languages/shakespeare/parser/parser.ts
Normal file
@ -0,0 +1,589 @@
|
||||
import { CstNode, CstParser, ParserMethod } from "chevrotain";
|
||||
import * as Tokens from "./tokens";
|
||||
|
||||
export interface RuleBook<T> {
|
||||
program: T;
|
||||
programTitle: T;
|
||||
characterIntro: T;
|
||||
actHeading: T;
|
||||
sceneHeading: T;
|
||||
entrance: T;
|
||||
exit: T;
|
||||
multiExit: T;
|
||||
entryExitClause: T;
|
||||
actSection: T;
|
||||
sceneSection: T;
|
||||
sceneSectionChunk: T;
|
||||
speakerClause: T;
|
||||
dialogueSet: T;
|
||||
dialogueLine: T;
|
||||
// ENGLISH
|
||||
noun: T;
|
||||
adjective: T;
|
||||
possessive: T;
|
||||
reflexive: T;
|
||||
comparative: T;
|
||||
// STATEMENTS
|
||||
assignment: T;
|
||||
exclaimAssignment: T;
|
||||
arithAssignment: T;
|
||||
stdin: T;
|
||||
stdout: T;
|
||||
goto: T;
|
||||
question: T;
|
||||
conditional: T;
|
||||
comparator: T;
|
||||
_asComparator: T;
|
||||
_simpleComparator: T;
|
||||
_moreLessComparator: T;
|
||||
// CONSTANTS
|
||||
constant: T;
|
||||
simpleConstant: T;
|
||||
verbalConstant: T;
|
||||
unarticulatedVerbalConstant: T;
|
||||
// EXPRESSION TREE
|
||||
expression: T;
|
||||
atomicExpression: T;
|
||||
sumExpression: T;
|
||||
differenceExpression: T;
|
||||
productExpression: T;
|
||||
quotientExpression: T;
|
||||
remainderExpression: T;
|
||||
factorialExpression: T;
|
||||
squareExpression: T;
|
||||
cubeExpression: T;
|
||||
squareRootExpression: T;
|
||||
twiceExpression: T;
|
||||
}
|
||||
|
||||
export class ShakespeareParser
|
||||
extends CstParser
|
||||
implements RuleBook<ParserMethod<[], CstNode>>
|
||||
{
|
||||
constructor() {
|
||||
super(Tokens.AllTokens, {
|
||||
nodeLocationTracking: "full",
|
||||
recoveryEnabled: true,
|
||||
});
|
||||
this.performSelfAnalysis();
|
||||
}
|
||||
|
||||
program = this.RULE("program", () => {
|
||||
this.SUBRULE(this.programTitle);
|
||||
this.MANY(() => this.CONSUME(Tokens.WhitespaceOrNewline));
|
||||
this.MANY1(() => this.SUBRULE(this.characterIntro));
|
||||
this.MANY2(() => this.SUBRULE(this.actSection));
|
||||
});
|
||||
|
||||
/** <description> */
|
||||
programTitle = this.RULE("programTitle", () => {
|
||||
this.MANY(() => this.CONSUME(Tokens.Word));
|
||||
this.CONSUME(Tokens.Period);
|
||||
});
|
||||
|
||||
/** `<character-name>, <description>` */
|
||||
characterIntro = this.RULE("characterIntro", () => {
|
||||
this.CONSUME(Tokens.Character);
|
||||
this.CONSUME(Tokens.Comma);
|
||||
this.MANY(() => this.CONSUME(Tokens.Word));
|
||||
this.CONSUME(Tokens.SentenceMark);
|
||||
});
|
||||
|
||||
/** `Act <RomanNumeral>: <description>` */
|
||||
actHeading = this.RULE("actHeading", () => {
|
||||
this.CONSUME(Tokens.Act);
|
||||
this.CONSUME(Tokens.RomanNumeral);
|
||||
this.CONSUME(Tokens.Colon);
|
||||
this.MANY(() => this.CONSUME(Tokens.Word));
|
||||
this.CONSUME(Tokens.SentenceMark);
|
||||
});
|
||||
|
||||
/** `Scene <RomanNumeral>: <description>` */
|
||||
sceneHeading = this.RULE("sceneHeading", () => {
|
||||
this.CONSUME(Tokens.Scene);
|
||||
this.CONSUME(Tokens.RomanNumeral);
|
||||
this.CONSUME(Tokens.Colon);
|
||||
this.MANY(() => this.CONSUME(Tokens.Word));
|
||||
this.CONSUME(Tokens.SentenceMark);
|
||||
});
|
||||
|
||||
/** `[Enter <character-name> (and <character-name>)]` */
|
||||
entrance = this.RULE("entrance", () => {
|
||||
this.CONSUME(Tokens.SquareBracketOpen);
|
||||
this.CONSUME(Tokens.Enter);
|
||||
this.CONSUME1(Tokens.Character);
|
||||
this.OPTION(() => {
|
||||
this.CONSUME(Tokens.And);
|
||||
this.CONSUME2(Tokens.Character);
|
||||
});
|
||||
this.CONSUME(Tokens.SquareBracketClose);
|
||||
});
|
||||
|
||||
/** `[Exit <character-name>]` */
|
||||
exit = this.RULE("exit", () => {
|
||||
this.CONSUME(Tokens.SquareBracketOpen);
|
||||
this.CONSUME(Tokens.Exit);
|
||||
this.CONSUME(Tokens.Character);
|
||||
this.CONSUME(Tokens.SquareBracketClose);
|
||||
});
|
||||
|
||||
/** `[Exeunt (<character-name> and <character-name>)]` */
|
||||
multiExit = this.RULE("multiExit", () => {
|
||||
this.CONSUME(Tokens.SquareBracketOpen);
|
||||
this.CONSUME(Tokens.Exeunt);
|
||||
this.OPTION(() => {
|
||||
this.CONSUME1(Tokens.Character);
|
||||
this.CONSUME(Tokens.And);
|
||||
this.CONSUME2(Tokens.Character);
|
||||
});
|
||||
this.CONSUME(Tokens.SquareBracketClose);
|
||||
});
|
||||
|
||||
/** Clause for entry or exit of characters */
|
||||
entryExitClause = this.RULE("entryExitClause", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.SUBRULE(this.entrance) },
|
||||
{ ALT: () => this.SUBRULE(this.exit) },
|
||||
{ ALT: () => this.SUBRULE(this.multiExit) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** Text corresponding to a single act */
|
||||
actSection = this.RULE("actSection", () => {
|
||||
this.SUBRULE(this.actHeading);
|
||||
this.AT_LEAST_ONE(() => this.SUBRULE(this.sceneSection));
|
||||
});
|
||||
|
||||
/** Text corresponding to a single scene */
|
||||
sceneSection = this.RULE("sceneSection", () => {
|
||||
this.SUBRULE(this.sceneHeading);
|
||||
this.AT_LEAST_ONE(() => this.SUBRULE(this.sceneSectionChunk));
|
||||
});
|
||||
|
||||
/** A single item of a scene: dialogue set or entry-exit clause */
|
||||
sceneSectionChunk = this.RULE("sceneSectionChunk", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.SUBRULE(this.entryExitClause) },
|
||||
{ ALT: () => this.SUBRULE(this.dialogueSet) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** `<character-name>:` */
|
||||
speakerClause = this.RULE("speakerClause", () => {
|
||||
this.CONSUME(Tokens.Character);
|
||||
this.CONSUME(Tokens.Colon);
|
||||
});
|
||||
|
||||
/** Set of dialogues spoken by a character */
|
||||
dialogueSet = this.RULE("dialogueSet", () => {
|
||||
this.SUBRULE(this.speakerClause);
|
||||
this.AT_LEAST_ONE(() => this.SUBRULE(this.dialogueLine));
|
||||
});
|
||||
|
||||
/** A single line of dialogue spoken by a character */
|
||||
dialogueLine = this.RULE("dialogueLine", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.SUBRULE(this.conditional) },
|
||||
{ ALT: () => this.SUBRULE(this.nonConditionalDialogueLine) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** Dialogue line possibilities, excluding conditionals */
|
||||
nonConditionalDialogueLine = this.RULE("nonConditionalDialogueLine", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.SUBRULE(this.assignment) },
|
||||
{ ALT: () => this.SUBRULE(this.stdin) },
|
||||
{ ALT: () => this.SUBRULE(this.stdout) },
|
||||
{ ALT: () => this.SUBRULE(this.goto) },
|
||||
{ ALT: () => this.SUBRULE(this.stackPush) },
|
||||
{ ALT: () => this.SUBRULE(this.stackPop) },
|
||||
{ ALT: () => this.SUBRULE(this.question) },
|
||||
]);
|
||||
});
|
||||
|
||||
///////////////////////////////
|
||||
////////// ENGLISH //////////
|
||||
///////////////////////////////
|
||||
|
||||
/** Shakespearean noun */
|
||||
noun = this.RULE("noun", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.CONSUME(Tokens.NegativeNoun) },
|
||||
{ ALT: () => this.CONSUME(Tokens.NeutralNoun) },
|
||||
{ ALT: () => this.CONSUME(Tokens.PositiveNoun) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** Shakesperean adjective */
|
||||
adjective = this.RULE("adjective", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.CONSUME(Tokens.NegativeAdjective) },
|
||||
{ ALT: () => this.CONSUME(Tokens.NeutralAdjective) },
|
||||
{ ALT: () => this.CONSUME(Tokens.PositiveAdjective) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** Any recognized possessive (my, your, his, her, etc.) */
|
||||
possessive = this.RULE("possessive", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.CONSUME(Tokens.FirstPersonPossessive) },
|
||||
{ ALT: () => this.CONSUME(Tokens.SecondPersonPossessive) },
|
||||
{ ALT: () => this.CONSUME(Tokens.ThirdPersonPossessive) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** Any recognized reflexive (myself, thyself, etc.) */
|
||||
reflexive = this.RULE("reflexive", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.CONSUME(Tokens.FirstPersonReflexive) },
|
||||
{ ALT: () => this.CONSUME(Tokens.SecondPersonReflexive) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** Any recognized comparative (better, punier, etc) */
|
||||
comparative = this.RULE("comparative", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.CONSUME(Tokens.PositiveComparative) },
|
||||
{ ALT: () => this.CONSUME(Tokens.NegativeComparative) },
|
||||
]);
|
||||
});
|
||||
|
||||
///////////////////////////////
|
||||
///////// STATEMENTS ////////
|
||||
///////////////////////////////
|
||||
|
||||
/** Assignment of an expression to a character */
|
||||
assignment = this.RULE("assignment", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.SUBRULE(this.exclaimAssignment) },
|
||||
{ ALT: () => this.SUBRULE(this.arithAssignment) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** `<second-person> <unarticulated-constant>` */
|
||||
exclaimAssignment = this.RULE("exclaimAssignment", () => {
|
||||
this.CONSUME(Tokens.SecondPerson);
|
||||
this.SUBRULE(this.unarticulatedVerbalConstant);
|
||||
this.CONSUME(Tokens.SentenceMark);
|
||||
});
|
||||
|
||||
/** `<second-person> <be> as <adjective> as <expression>` */
|
||||
arithAssignment = this.RULE("arithAssignment", () => {
|
||||
this.CONSUME(Tokens.SecondPerson);
|
||||
this.CONSUME(Tokens.Be);
|
||||
this.OPTION(() => {
|
||||
this.CONSUME1(Tokens.As);
|
||||
this.SUBRULE(this.adjective);
|
||||
this.CONSUME2(Tokens.As);
|
||||
});
|
||||
this.SUBRULE(this.expression);
|
||||
this.CONSUME(Tokens.SentenceMark);
|
||||
});
|
||||
|
||||
/** `Listen to your heart | Open your mind` */
|
||||
stdin = this.RULE("stdin", () => {
|
||||
this.OR([
|
||||
{
|
||||
ALT: () => {
|
||||
this.CONSUME(Tokens.Listen);
|
||||
this.CONSUME(Tokens.To);
|
||||
this.CONSUME1(Tokens.SecondPersonPossessive);
|
||||
this.CONSUME(Tokens.Heart);
|
||||
},
|
||||
},
|
||||
{
|
||||
ALT: () => {
|
||||
this.CONSUME(Tokens.Open);
|
||||
this.CONSUME2(Tokens.SecondPersonPossessive);
|
||||
this.CONSUME(Tokens.Mind);
|
||||
},
|
||||
},
|
||||
]);
|
||||
this.CONSUME(Tokens.SentenceMark);
|
||||
});
|
||||
|
||||
/** `Open <your> heart | Speak <your> mind` */
|
||||
stdout = this.RULE("stdout", () => {
|
||||
this.OR([
|
||||
{
|
||||
ALT: () => {
|
||||
this.CONSUME(Tokens.Open);
|
||||
this.CONSUME1(Tokens.SecondPersonPossessive);
|
||||
this.CONSUME(Tokens.Heart);
|
||||
},
|
||||
},
|
||||
{
|
||||
ALT: () => {
|
||||
this.CONSUME(Tokens.Speak);
|
||||
this.CONSUME2(Tokens.SecondPersonPossessive);
|
||||
this.CONSUME(Tokens.Mind);
|
||||
},
|
||||
},
|
||||
]);
|
||||
this.CONSUME(Tokens.SentenceMark);
|
||||
});
|
||||
|
||||
/** (let us|we shall|we must) (return|proceed) to (act|scene) <roman-numeral> */
|
||||
goto = this.RULE("goto", () => {
|
||||
this.OR1([
|
||||
{
|
||||
ALT: () => {
|
||||
this.CONSUME(Tokens.Let);
|
||||
this.CONSUME(Tokens.Us);
|
||||
},
|
||||
},
|
||||
{
|
||||
ALT: () => {
|
||||
this.CONSUME1(Tokens.We);
|
||||
this.CONSUME(Tokens.Shall);
|
||||
},
|
||||
},
|
||||
{
|
||||
ALT: () => {
|
||||
this.CONSUME2(Tokens.We);
|
||||
this.CONSUME(Tokens.Must);
|
||||
},
|
||||
},
|
||||
]);
|
||||
this.OR2([
|
||||
{ ALT: () => this.CONSUME(Tokens.Return) },
|
||||
{ ALT: () => this.CONSUME(Tokens.Proceed) },
|
||||
]);
|
||||
this.CONSUME(Tokens.To);
|
||||
this.OR3([
|
||||
{ ALT: () => this.CONSUME(Tokens.Act) },
|
||||
{ ALT: () => this.CONSUME(Tokens.Scene) },
|
||||
]);
|
||||
this.CONSUME(Tokens.RomanNumeral);
|
||||
this.CONSUME(Tokens.SentenceMark);
|
||||
});
|
||||
|
||||
/** `Remember <expression>` */
|
||||
stackPush = this.RULE("stackPush", () => {
|
||||
this.CONSUME(Tokens.Remember);
|
||||
this.SUBRULE(this.expression);
|
||||
this.CONSUME(Tokens.SentenceMark);
|
||||
});
|
||||
|
||||
/** `Recall <word>*` */
|
||||
stackPop = this.RULE("stackPop", () => {
|
||||
this.CONSUME(Tokens.Recall);
|
||||
this.MANY(() => this.CONSUME(Tokens.Word));
|
||||
this.CONSUME(Tokens.SentenceMark);
|
||||
});
|
||||
|
||||
/** `<be> <expression> <comparator> <expression>?` */
|
||||
question = this.RULE("question", () => {
|
||||
this.CONSUME(Tokens.Be);
|
||||
this.SUBRULE1(this.expression, { LABEL: "lhs" });
|
||||
this.SUBRULE(this.comparator);
|
||||
this.SUBRULE2(this.expression, { LABEL: "rhs" });
|
||||
this.CONSUME(Tokens.QuestionMark);
|
||||
});
|
||||
|
||||
/** `If so, <non-conditional-dialogue-line>` */
|
||||
conditional = this.RULE("conditional", () => {
|
||||
this.CONSUME(Tokens.If);
|
||||
this.OR([
|
||||
{ ALT: () => this.CONSUME(Tokens.So) },
|
||||
{ ALT: () => this.CONSUME(Tokens.Not) },
|
||||
]);
|
||||
this.CONSUME(Tokens.Comma);
|
||||
this.SUBRULE(this.nonConditionalDialogueLine);
|
||||
});
|
||||
|
||||
/** Comparator clause used in questions */
|
||||
comparator = this.RULE("comparator", () => {
|
||||
this.OPTION(() => this.CONSUME(Tokens.Not));
|
||||
this.OR([
|
||||
{ ALT: () => this.SUBRULE(this._asComparator) },
|
||||
{ ALT: () => this.SUBRULE(this._simpleComparator) },
|
||||
{ ALT: () => this.SUBRULE(this._moreLessComparator) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** `as <adjective> as` */
|
||||
_asComparator = this.RULE("_asComparator", () => {
|
||||
this.CONSUME1(Tokens.As);
|
||||
this.SUBRULE(this.adjective);
|
||||
this.CONSUME2(Tokens.As);
|
||||
});
|
||||
|
||||
/** `<comparative> than` */
|
||||
_simpleComparator = this.RULE("_simpleComparator", () => {
|
||||
this.SUBRULE(this.comparative);
|
||||
this.CONSUME(Tokens.Than);
|
||||
});
|
||||
|
||||
/** `(more|less) <adjective> than` */
|
||||
_moreLessComparator = this.RULE("_moreLessComparator", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.CONSUME(Tokens.More) },
|
||||
{ ALT: () => this.CONSUME(Tokens.Less) },
|
||||
]);
|
||||
this.SUBRULE(this.adjective);
|
||||
this.CONSUME(Tokens.Than);
|
||||
});
|
||||
|
||||
///////////////////////////////
|
||||
///////// CONSTANTS /////////
|
||||
///////////////////////////////
|
||||
|
||||
/** Constant expressions */
|
||||
constant = this.RULE("constant", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.SUBRULE(this.simpleConstant) },
|
||||
{ ALT: () => this.SUBRULE(this.verbalConstant) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** Simple keyword-based constant */
|
||||
simpleConstant = this.RULE("_simpleConstant", () => {
|
||||
this.CONSUME(Tokens.Nothing);
|
||||
});
|
||||
|
||||
/** Verbally expressed constant */
|
||||
verbalConstant = this.RULE("_verbalConstant", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.CONSUME(Tokens.Article) },
|
||||
{ ALT: () => this.SUBRULE(this.possessive) },
|
||||
]);
|
||||
this.SUBRULE(this.unarticulatedVerbalConstant);
|
||||
});
|
||||
|
||||
/** `<adjective>* <noun>`, representing a constant */
|
||||
unarticulatedVerbalConstant = this.RULE("unarticulatedVerbalConstant", () => {
|
||||
// Shakespeare only allows non-negative adjectives on positive nouns and
|
||||
// negative adjectives on negative nouns.
|
||||
//
|
||||
// Unfortunately, positive and negative branches cannot be separated in this
|
||||
// parser since they share an arbitrarily long prefix of neutral adjectives.
|
||||
// Thus, this branch validation must be done in the CST visitor.
|
||||
this.MANY(() => this.SUBRULE(this.adjective));
|
||||
this.SUBRULE(this.noun);
|
||||
});
|
||||
|
||||
///////////////////////////////
|
||||
////// EXPRESSION TREE //////
|
||||
///////////////////////////////
|
||||
|
||||
/** Root node of an expression tree */
|
||||
expression = this.RULE("expression", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.SUBRULE(this.sumExpression) },
|
||||
{ ALT: () => this.SUBRULE(this.differenceExpression) },
|
||||
{ ALT: () => this.SUBRULE(this.productExpression) },
|
||||
{ ALT: () => this.SUBRULE(this.quotientExpression) },
|
||||
{ ALT: () => this.SUBRULE(this.remainderExpression) },
|
||||
{ ALT: () => this.SUBRULE(this.factorialExpression) },
|
||||
{ ALT: () => this.SUBRULE(this.squareExpression) },
|
||||
{ ALT: () => this.SUBRULE(this.squareRootExpression) },
|
||||
{ ALT: () => this.SUBRULE(this.cubeExpression) },
|
||||
{ ALT: () => this.SUBRULE(this.twiceExpression) },
|
||||
{ ALT: () => this.SUBRULE(this.atomicExpression) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** `<character> | <reflexive> | <constant>` */
|
||||
atomicExpression = this.RULE("atomicExpression", () => {
|
||||
this.OR([
|
||||
{ ALT: () => this.CONSUME(Tokens.Character) },
|
||||
{ ALT: () => this.CONSUME(Tokens.FirstPerson) },
|
||||
{ ALT: () => this.CONSUME(Tokens.SecondPerson) },
|
||||
{ ALT: () => this.SUBRULE(this.reflexive) },
|
||||
{ ALT: () => this.SUBRULE(this.constant) },
|
||||
]);
|
||||
});
|
||||
|
||||
/** `the sum of <expression> and <expression>` */
|
||||
sumExpression = this.RULE("sumExpression", () => {
|
||||
this.CONSUME(Tokens.The);
|
||||
this.CONSUME(Tokens.Sum);
|
||||
this.CONSUME(Tokens.Of);
|
||||
this.SUBRULE1(this.expression, { LABEL: "lhs" });
|
||||
this.CONSUME(Tokens.And);
|
||||
this.SUBRULE2(this.expression, { LABEL: "rhs" });
|
||||
});
|
||||
|
||||
/** `the difference between <expression> and <expression>` */
|
||||
differenceExpression = this.RULE("differenceExpression", () => {
|
||||
this.CONSUME(Tokens.The);
|
||||
this.CONSUME(Tokens.Difference);
|
||||
this.CONSUME(Tokens.Between);
|
||||
this.SUBRULE1(this.expression, { LABEL: "lhs" });
|
||||
this.CONSUME(Tokens.And);
|
||||
this.SUBRULE2(this.expression, { LABEL: "rhs" });
|
||||
});
|
||||
|
||||
/** `the product of <expression> and <expression>` */
|
||||
productExpression = this.RULE("productExpression", () => {
|
||||
this.CONSUME(Tokens.The);
|
||||
this.CONSUME(Tokens.Product);
|
||||
this.CONSUME(Tokens.Of);
|
||||
this.SUBRULE1(this.expression, { LABEL: "lhs" });
|
||||
this.CONSUME(Tokens.And);
|
||||
this.SUBRULE2(this.expression, { LABEL: "rhs" });
|
||||
});
|
||||
|
||||
/** `the quotient between <expression> and <expression>` */
|
||||
quotientExpression = this.RULE("quotientExpression", () => {
|
||||
this.CONSUME(Tokens.The);
|
||||
this.CONSUME(Tokens.Quotient);
|
||||
this.CONSUME(Tokens.Between);
|
||||
this.SUBRULE1(this.expression, { LABEL: "lhs" });
|
||||
this.CONSUME(Tokens.And);
|
||||
this.SUBRULE2(this.expression, { LABEL: "rhs" });
|
||||
});
|
||||
|
||||
/** `the remainder of the quotient between <expression> and <expression>` */
|
||||
remainderExpression = this.RULE("remainderExpression", () => {
|
||||
this.CONSUME1(Tokens.The);
|
||||
this.CONSUME(Tokens.Remainder);
|
||||
this.CONSUME(Tokens.Of);
|
||||
this.CONSUME2(Tokens.The);
|
||||
this.CONSUME(Tokens.Quotient);
|
||||
this.CONSUME(Tokens.Between);
|
||||
this.SUBRULE1(this.expression, { LABEL: "lhs" });
|
||||
this.CONSUME(Tokens.And);
|
||||
this.SUBRULE2(this.expression, { LABEL: "rhs" });
|
||||
});
|
||||
|
||||
/** `the factorial of <expression>` */
|
||||
factorialExpression = this.RULE("factorialExpression", () => {
|
||||
this.CONSUME(Tokens.The);
|
||||
this.CONSUME(Tokens.Factorial);
|
||||
this.CONSUME(Tokens.Of);
|
||||
this.SUBRULE(this.expression);
|
||||
});
|
||||
|
||||
/** `the square of <expression>` */
|
||||
squareExpression = this.RULE("squareExpression", () => {
|
||||
this.CONSUME(Tokens.The);
|
||||
this.CONSUME(Tokens.Square);
|
||||
this.CONSUME(Tokens.Of);
|
||||
this.SUBRULE(this.expression);
|
||||
});
|
||||
|
||||
/** `the cube of <expression>` */
|
||||
cubeExpression = this.RULE("cubeExpression", () => {
|
||||
this.CONSUME(Tokens.The);
|
||||
this.CONSUME(Tokens.Cube);
|
||||
this.CONSUME(Tokens.Of);
|
||||
this.SUBRULE(this.expression);
|
||||
});
|
||||
|
||||
/** `the square root of <expression>` */
|
||||
squareRootExpression = this.RULE("squareRootExpression", () => {
|
||||
this.CONSUME(Tokens.The);
|
||||
this.CONSUME(Tokens.Square);
|
||||
this.CONSUME(Tokens.Root);
|
||||
this.CONSUME(Tokens.Of);
|
||||
this.SUBRULE(this.expression);
|
||||
});
|
||||
|
||||
/** `twice <expression>` */
|
||||
twiceExpression = this.RULE("twiceExpression", () => {
|
||||
this.CONSUME(Tokens.Twice);
|
||||
this.SUBRULE(this.expression);
|
||||
});
|
||||
}
|
509
languages/shakespeare/parser/tokens.ts
Normal file
509
languages/shakespeare/parser/tokens.ts
Normal file
@ -0,0 +1,509 @@
|
||||
import { createToken, Lexer } from "chevrotain";
|
||||
import * as R from "./constants";
|
||||
|
||||
export const Word = createToken({
|
||||
name: "Word",
|
||||
pattern: /[^\s\n\.\?\!]+/,
|
||||
});
|
||||
|
||||
export const WhitespaceOrNewline = createToken({
|
||||
name: "WhitespaceOrNewline",
|
||||
pattern: /[\s\n]+/,
|
||||
group: Lexer.SKIPPED,
|
||||
});
|
||||
|
||||
export const Whitespace = createToken({
|
||||
name: "Whitespace",
|
||||
pattern: /\s+/,
|
||||
categories: [WhitespaceOrNewline],
|
||||
group: Lexer.SKIPPED,
|
||||
});
|
||||
|
||||
export const RomanNumeral = createToken({
|
||||
name: "RomanNumeral",
|
||||
pattern: /[IVXLCDM]+\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Enter = createToken({
|
||||
name: "Enter",
|
||||
pattern: /enter\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Exit = createToken({
|
||||
name: "Exit",
|
||||
pattern: /exit\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Exeunt = createToken({
|
||||
name: "Exeunt",
|
||||
pattern: /exeunt\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const And = createToken({
|
||||
name: "And",
|
||||
pattern: /and\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Article = createToken({
|
||||
name: "Article",
|
||||
pattern: /(a|an|the)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Be = createToken({
|
||||
name: "Be",
|
||||
pattern: /(am|are|art|be|is)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Not = createToken({
|
||||
name: "Not",
|
||||
pattern: /not\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const More = createToken({
|
||||
name: "More",
|
||||
pattern: /more\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Less = createToken({
|
||||
name: "Less",
|
||||
pattern: /less\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Than = createToken({
|
||||
name: "Than",
|
||||
pattern: /than\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Let = createToken({
|
||||
name: "Let",
|
||||
pattern: /let\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Us = createToken({
|
||||
name: "Us",
|
||||
pattern: /us\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const We = createToken({
|
||||
name: "We",
|
||||
pattern: /we\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Shall = createToken({
|
||||
name: "Shall",
|
||||
pattern: /shall\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Must = createToken({
|
||||
name: "Must",
|
||||
pattern: /must\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Return = createToken({
|
||||
name: "Return",
|
||||
pattern: /return\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Proceed = createToken({
|
||||
name: "Proceed",
|
||||
pattern: /proceed\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Character = createToken({
|
||||
name: "Character",
|
||||
pattern: R.CharacterRegex,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const FirstPersonPossessive = createToken({
|
||||
name: "FirstPersonPossessive",
|
||||
pattern: /(mine|my)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const FirstPersonReflexive = createToken({
|
||||
name: "FirstPersonReflexive",
|
||||
pattern: /myself\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const FirstPerson = createToken({
|
||||
name: "FirstPerson",
|
||||
pattern: /(i|me)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const NegativeAdjective = createToken({
|
||||
name: "NegativeAdjective",
|
||||
pattern: R.NegativeAdjectiveRegex,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const NegativeComparative = createToken({
|
||||
name: "NegativeComparative",
|
||||
pattern: /(punier|smaller|worse)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const NegativeNoun = createToken({
|
||||
name: "NegativeNoun",
|
||||
pattern: R.NegativeNounRegex,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const NeutralAdjective = createToken({
|
||||
name: "NeutralAdjective",
|
||||
pattern: R.NeutralAdjectiveRegex,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const NeutralNoun = createToken({
|
||||
name: "NeutralNoun",
|
||||
pattern: R.NeutralNounRegex,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Nothing = createToken({
|
||||
name: "Nothing",
|
||||
pattern: /(nothing|zero)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const PositiveAdjective = createToken({
|
||||
name: "PositiveAdjective",
|
||||
pattern: R.PositiveAdjectiveRegex,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const PositiveComparative = createToken({
|
||||
name: "PositiveComparative",
|
||||
pattern: /(better|bigger|fresher|friendlier|nicer|jollier)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const PositiveNoun = createToken({
|
||||
name: "PositiveNoun",
|
||||
pattern: R.PositiveNounRegex,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const SecondPersonPossessive = createToken({
|
||||
name: "SecondPersonPossessive",
|
||||
pattern: /(thine|thy|your)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const SecondPersonReflexive = createToken({
|
||||
name: "SecondPersonReflexive",
|
||||
pattern: /(thyself|yourself)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const SecondPerson = createToken({
|
||||
name: "SecondPerson",
|
||||
pattern: /(thee|thou|you)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const ThirdPersonPossessive = createToken({
|
||||
name: "ThirdPersonPossessive",
|
||||
pattern: /(his|her|its|their)\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Act = createToken({
|
||||
name: "Act",
|
||||
pattern: /act\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Scene = createToken({
|
||||
name: "Scene",
|
||||
pattern: /scene\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const As = createToken({
|
||||
name: "As",
|
||||
pattern: /as\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Open = createToken({
|
||||
name: "Open",
|
||||
pattern: /open\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Heart = createToken({
|
||||
name: "Heart",
|
||||
pattern: /heart\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Speak = createToken({
|
||||
name: "Speak",
|
||||
pattern: /speak\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Mind = createToken({
|
||||
name: "Mind",
|
||||
pattern: /mind\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const The = createToken({
|
||||
name: "The",
|
||||
pattern: /the\b/i,
|
||||
categories: [Word, Article],
|
||||
});
|
||||
|
||||
export const Between = createToken({
|
||||
name: "Between",
|
||||
pattern: /between\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Of = createToken({
|
||||
name: "Of",
|
||||
pattern: /of\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const If = createToken({
|
||||
name: "If",
|
||||
pattern: /if\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const So = createToken({
|
||||
name: "So",
|
||||
pattern: /so\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const To = createToken({
|
||||
name: "To",
|
||||
pattern: /to\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Listen = createToken({
|
||||
name: "Listen",
|
||||
pattern: /listen\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Recall = createToken({
|
||||
name: "Recall",
|
||||
pattern: /recall\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Remember = createToken({
|
||||
name: "Remember",
|
||||
pattern: /remember\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
////////////////////////////////
|
||||
/////////// OPERATORS //////////
|
||||
////////////////////////////////
|
||||
|
||||
export const Sum = createToken({
|
||||
name: "Sum",
|
||||
pattern: /sum\b/,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Difference = createToken({
|
||||
name: "Difference",
|
||||
pattern: /difference\b/,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Product = createToken({
|
||||
name: "Product",
|
||||
pattern: /product\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Quotient = createToken({
|
||||
name: "Quotient",
|
||||
pattern: /quotient\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Remainder = createToken({
|
||||
name: "Remainder",
|
||||
pattern: /remainder\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Factorial = createToken({
|
||||
name: "Factorial",
|
||||
pattern: /factorial\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Square = createToken({
|
||||
name: "Square",
|
||||
pattern: /square\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Root = createToken({
|
||||
name: "Root",
|
||||
pattern: /root\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Cube = createToken({
|
||||
name: "Cube",
|
||||
pattern: /cube\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Twice = createToken({
|
||||
name: "Twice",
|
||||
pattern: /twice\b/i,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const SentenceMark = createToken({
|
||||
name: "SentenceMark",
|
||||
pattern: /\.|\?|\!/,
|
||||
});
|
||||
|
||||
export const SquareBracketOpen = createToken({
|
||||
name: "SquareBracketOpen",
|
||||
pattern: /\[/,
|
||||
});
|
||||
|
||||
export const SquareBracketClose = createToken({
|
||||
name: "SquareBracketClose",
|
||||
pattern: /\]/,
|
||||
});
|
||||
|
||||
export const Colon = createToken({
|
||||
name: "Colon",
|
||||
pattern: /:/,
|
||||
});
|
||||
|
||||
export const Comma = createToken({
|
||||
name: "Comma",
|
||||
pattern: /,/,
|
||||
categories: [Word],
|
||||
});
|
||||
|
||||
export const Period = createToken({
|
||||
name: "Period",
|
||||
pattern: /\./,
|
||||
categories: [SentenceMark],
|
||||
});
|
||||
|
||||
export const QuestionMark = createToken({
|
||||
name: "QuestionMark",
|
||||
pattern: /\?/,
|
||||
categories: [SentenceMark],
|
||||
});
|
||||
|
||||
export const I = createToken({
|
||||
name: "I",
|
||||
pattern: /i\b/i,
|
||||
categories: [RomanNumeral, FirstPerson, Word],
|
||||
});
|
||||
|
||||
export const AllTokens = [
|
||||
Whitespace,
|
||||
WhitespaceOrNewline,
|
||||
Character,
|
||||
The,
|
||||
Be,
|
||||
More,
|
||||
Less,
|
||||
Than,
|
||||
Let,
|
||||
Us,
|
||||
We,
|
||||
Shall,
|
||||
Must,
|
||||
Return,
|
||||
Proceed,
|
||||
Not,
|
||||
As,
|
||||
I,
|
||||
If,
|
||||
So,
|
||||
To,
|
||||
Enter,
|
||||
Exit,
|
||||
Exeunt,
|
||||
Open,
|
||||
Heart,
|
||||
Speak,
|
||||
Mind,
|
||||
Listen,
|
||||
Recall,
|
||||
Remember,
|
||||
And,
|
||||
Nothing,
|
||||
Between,
|
||||
Of,
|
||||
Sum,
|
||||
Difference,
|
||||
Product,
|
||||
Quotient,
|
||||
Remainder,
|
||||
Factorial,
|
||||
Square,
|
||||
Root,
|
||||
Cube,
|
||||
Twice,
|
||||
Period,
|
||||
QuestionMark,
|
||||
Article,
|
||||
Act,
|
||||
Scene,
|
||||
Comma,
|
||||
Colon,
|
||||
SquareBracketOpen,
|
||||
SquareBracketClose,
|
||||
SentenceMark,
|
||||
FirstPersonPossessive,
|
||||
FirstPersonReflexive,
|
||||
FirstPerson,
|
||||
NegativeAdjective,
|
||||
NegativeComparative,
|
||||
NegativeNoun,
|
||||
NeutralAdjective,
|
||||
NeutralNoun,
|
||||
PositiveAdjective,
|
||||
PositiveComparative,
|
||||
PositiveNoun,
|
||||
SecondPersonPossessive,
|
||||
SecondPersonReflexive,
|
||||
SecondPerson,
|
||||
ThirdPersonPossessive,
|
||||
RomanNumeral,
|
||||
Word,
|
||||
];
|
323
languages/shakespeare/parser/visitor-types.d.ts
vendored
Normal file
323
languages/shakespeare/parser/visitor-types.d.ts
vendored
Normal file
@ -0,0 +1,323 @@
|
||||
import { DocumentRange } from "../../../types";
|
||||
import * as C from "./cst";
|
||||
|
||||
/** Type that contains information about what AST the visitor returns for each rule */
|
||||
export type ShakespeareVisitorTypes = {
|
||||
program(children: C.ProgramCstChildren, param?: any): Program;
|
||||
programTitle(children: C.ProgramTitleCstChildren, param?: any): null;
|
||||
characterIntro(
|
||||
children: C.CharacterIntroCstChildren,
|
||||
param?: any
|
||||
): CharacterIntro;
|
||||
actHeading(children: C.ActHeadingCstChildren, param?: any): ActHeading;
|
||||
sceneHeading(children: C.SceneHeadingCstChildren, param?: any): SceneHeading;
|
||||
entrance(children: C.EntranceCstChildren, param?: any): EntryExitClause;
|
||||
exit(children: C.ExitCstChildren, param?: any): EntryExitClause;
|
||||
multiExit(children: C.MultiExitCstChildren, param?: any): EntryExitClause;
|
||||
entryExitClause(
|
||||
children: C.EntryExitClauseCstChildren,
|
||||
param?: any
|
||||
): EntryExitClause;
|
||||
actSection(children: C.ActSectionCstChildren, param?: any): ActSection;
|
||||
sceneSection(children: C.SceneSectionCstChildren, param?: any): SceneSection;
|
||||
sceneSectionChunk(
|
||||
children: C.SceneSectionChunkCstChildren,
|
||||
param?: any
|
||||
): SceneSectionChunk;
|
||||
speakerClause(
|
||||
children: C.SpeakerClauseCstChildren,
|
||||
param?: any
|
||||
): SpeakerClause;
|
||||
dialogueSet(children: C.DialogueSetCstChildren, param?: any): DialogueSet;
|
||||
dialogueLine(children: C.DialogueCstChildren, param?: any): DialogueLine;
|
||||
nonConditionalDialogueLine(
|
||||
children: C.NonConditionalDialogueLineCstChildren,
|
||||
param?: any
|
||||
): NonConditionalDialogueLine;
|
||||
|
||||
/////////// ENGLISH ///////////
|
||||
noun(children: C.NounCstChildren, param?: any): 1 | 0 | -1;
|
||||
adjective(children: C.AdjectiveCstChildren, param?: any): 1 | 0 | -1;
|
||||
possessive(
|
||||
children: C.PossessiveCstChildren,
|
||||
param?: any
|
||||
): "first" | "second" | "third";
|
||||
reflexive(children: C.ReflexiveCstChildren, param?: any): "first" | "second";
|
||||
comparative(children: C.ComparativeCstChildren, param?: any): 1 | -1;
|
||||
|
||||
/////////// STATEMENTS ///////////
|
||||
assignment(children: C.AssignmentCstChildren, param?: any): AssignmentLine;
|
||||
exclaimAssignment(
|
||||
children: C.ExclaimAssignmentCstChildren,
|
||||
param?: any
|
||||
): AssignmentLine;
|
||||
arithAssignment(
|
||||
children: C.ArithAssignmentCstChildren,
|
||||
param?: any
|
||||
): AssignmentLine;
|
||||
stdin(children: C.StdinCstChildren, param?: any): StdinLine;
|
||||
stdout(children: C.StdoutCstChildren, param?: any): StdoutLine;
|
||||
goto(children: C.GotoCstChildren, param?: any): GotoLine;
|
||||
stackPush(children: C.StackPushCstChildren, param?: any): StackPushLine;
|
||||
stackPop(children: C.StackPopCstChildren, param?: any): StackPopLine;
|
||||
question(children: C.QuestionCstChildren, param?: any): QuestionLine;
|
||||
conditional(children: C.ConditionalCstChildren, param?: any): ConditionalLine;
|
||||
comparator(
|
||||
children: C.ComparatorCstChildren,
|
||||
param?: any
|
||||
): ComparisonOperator;
|
||||
_asComparator(
|
||||
children: C._asComparatorCstChildren,
|
||||
param?: any
|
||||
): ComparisonOperator;
|
||||
_simpleComparator(
|
||||
children: C._simpleComparatorCstChildren,
|
||||
param?: any
|
||||
): ComparisonOperator;
|
||||
_moreLessComparator(
|
||||
children: C._moreLessComparatorCstChildren,
|
||||
param?: any
|
||||
): ComparisonOperator;
|
||||
|
||||
////////////// CONSTANTS //////////////
|
||||
constant(children: C.ConstantCstChildren, param?: any): Constant;
|
||||
_simpleConstant(
|
||||
children: C._simpleConstantCstChildren,
|
||||
param?: any
|
||||
): Constant;
|
||||
_verbalConstant(
|
||||
children: C._verbalConstantCstChildren,
|
||||
param?: any
|
||||
): Constant;
|
||||
unarticulatedVerbalConstant(
|
||||
children: C.UnarticulatedVerbalConstantCstChildren,
|
||||
param?: any
|
||||
): Constant;
|
||||
|
||||
/////////// EXPRESSION TREE ///////////
|
||||
expression(children: C.ExpressionCstChildren, param?: any): Expression;
|
||||
atomicExpression(
|
||||
children: C.AtomicExpressionCstChildren,
|
||||
param?: any
|
||||
): AtomicExpression;
|
||||
sumExpression(
|
||||
children: C.SumExpressionCstChildren,
|
||||
param?: any
|
||||
): BinaryExpression;
|
||||
differenceExpression(
|
||||
children: C.DifferenceExpressionCstChildren,
|
||||
param?: any
|
||||
): BinaryExpression;
|
||||
productExpression(
|
||||
children: C.ProductExpressionCstChildren,
|
||||
param?: any
|
||||
): BinaryExpression;
|
||||
quotientExpression(
|
||||
children: C.QuotientExpressionCstChildren,
|
||||
param?: any
|
||||
): BinaryExpression;
|
||||
remainderExpression(
|
||||
children: C.RemainderExpressionCstChildren,
|
||||
param?: any
|
||||
): BinaryExpression;
|
||||
factorialExpression(
|
||||
children: C.FactorialExpressionCstChildren,
|
||||
param?: any
|
||||
): UnaryExpression;
|
||||
squareExpression(
|
||||
children: C.SquareExpressionCstChildren,
|
||||
param?: any
|
||||
): UnaryExpression;
|
||||
cubeExpression(
|
||||
children: C.CubeExpressionCstChildren,
|
||||
param?: any
|
||||
): UnaryExpression;
|
||||
squareRootExpression(
|
||||
children: C.SquareRootExpressionCstChildren,
|
||||
param?: any
|
||||
): UnaryExpression;
|
||||
twiceExpression(
|
||||
children: C.TwiceExpressionCstChildren,
|
||||
param?: any
|
||||
): UnaryExpression;
|
||||
};
|
||||
|
||||
/** We don't parse roman numerals and instead just treat them as string IDs */
|
||||
export type RomanNumeral = string;
|
||||
|
||||
/** AST for a complete Shakespeare program */
|
||||
export type Program = {
|
||||
characters: CharacterIntro[];
|
||||
acts: ActSection[];
|
||||
};
|
||||
|
||||
/** Character declaration at the start of a program */
|
||||
export type CharacterIntro = Character;
|
||||
|
||||
/** Heading of an act - only the act number is kept */
|
||||
export type ActHeading = RomanNumeral;
|
||||
/** Heading of a scene - only the scene number is kept */
|
||||
export type SceneHeading = RomanNumeral;
|
||||
|
||||
/** Clause for entry or exit of one or more characters */
|
||||
export type EntryExitClause = {
|
||||
type: "entry" | "exit";
|
||||
/**
|
||||
* List of characters entering or leaving.
|
||||
* `null` is used for `[Exeunt]` clauses.
|
||||
*/
|
||||
characters: Character[] | null;
|
||||
range: DocumentRange;
|
||||
};
|
||||
|
||||
/** Details of a single act */
|
||||
export type ActSection = {
|
||||
id: ActHeading;
|
||||
scenes: SceneSection[];
|
||||
};
|
||||
|
||||
/** Details of a single scene */
|
||||
export type SceneSection = {
|
||||
id: SceneHeading;
|
||||
items: SceneSectionItem[];
|
||||
};
|
||||
|
||||
/** An execution atom of a single scene */
|
||||
export type SceneSectionItem = EntryExitClause | DialogueItem;
|
||||
|
||||
/** A dialogue set or entry-exit clause belonging to a scene */
|
||||
export type SceneSectionChunk = EntryExitClause | DialogueSet;
|
||||
|
||||
/** `<character-name>:` clause that starts a dialogue set */
|
||||
export type SpeakerClause = Character;
|
||||
|
||||
/** Set of dialogues spoken by a character */
|
||||
export type DialogueSet = {
|
||||
type: "dialogue-set";
|
||||
speaker: SpeakerClause;
|
||||
lines: DialogueLine[];
|
||||
};
|
||||
|
||||
/** A single dialogue item containing speaker and line */
|
||||
export type DialogueItem = {
|
||||
type: "dialogue-item";
|
||||
speaker: Character;
|
||||
line: DialogueLine;
|
||||
};
|
||||
|
||||
/** Single dialogue line spoken by the current character */
|
||||
export type DialogueLine = NonConditionalDialogueLine | ConditionalLine;
|
||||
|
||||
/** Dialogue line excluding conditional */
|
||||
export type NonConditionalDialogueLine =
|
||||
| AssignmentLine
|
||||
| StdinLine
|
||||
| StdoutLine
|
||||
| GotoLine
|
||||
| StackPushLine
|
||||
| StackPopLine
|
||||
| QuestionLine;
|
||||
|
||||
/** Dialogue line representing an assignment operation */
|
||||
export type AssignmentLine = {
|
||||
type: "assign";
|
||||
value: Expression;
|
||||
range: DocumentRange;
|
||||
};
|
||||
|
||||
/** Dialogue line representing an STDOUT operation */
|
||||
export type StdoutLine = {
|
||||
type: "stdout";
|
||||
outType: "num" | "char";
|
||||
range: DocumentRange;
|
||||
};
|
||||
|
||||
/** Dialogue line representing an STDIN operation */
|
||||
export type StdinLine = {
|
||||
type: "stdin";
|
||||
inType: "num" | "char";
|
||||
range: DocumentRange;
|
||||
};
|
||||
|
||||
/** Dialogue line representing a goto operation */
|
||||
export type GotoLine = {
|
||||
type: "goto";
|
||||
targetType: "act" | "scene";
|
||||
target: RomanNumeral;
|
||||
range: DocumentRange;
|
||||
};
|
||||
|
||||
/** Dialogue line representing a stack push */
|
||||
export type StackPushLine = {
|
||||
type: "stack-push";
|
||||
expr: Expression;
|
||||
range: DocumentRange;
|
||||
};
|
||||
|
||||
/** Dialogue line representing a stack pop */
|
||||
export type StackPopLine = {
|
||||
type: "stack-pop";
|
||||
range: DocumentRange;
|
||||
};
|
||||
|
||||
/** Dialogue line representing a question */
|
||||
export type QuestionLine = {
|
||||
type: "question";
|
||||
comparator: ComparisonOperator;
|
||||
lhs: Expression;
|
||||
rhs: Expression;
|
||||
range: DocumentRange;
|
||||
};
|
||||
|
||||
/** Dialogue line representing a conditional (`If so/not, ...`) */
|
||||
export type ConditionalLine = {
|
||||
type: "conditional";
|
||||
invert: boolean;
|
||||
consequent: NonConditionalDialogueLine;
|
||||
range: DocumentRange;
|
||||
};
|
||||
|
||||
/** Comparison operator used in a question */
|
||||
export type ComparisonOperator = {
|
||||
invert?: boolean;
|
||||
type: "==" | ">" | "<";
|
||||
};
|
||||
|
||||
/** Verbal or simple constants evaluate to number */
|
||||
export type Constant = {
|
||||
type: "constant";
|
||||
value: number;
|
||||
};
|
||||
|
||||
/** Name of a character */
|
||||
export type Character = {
|
||||
type: "character";
|
||||
name: string;
|
||||
};
|
||||
|
||||
/** Reference to a character with pronoun/reflexive */
|
||||
export type CharacterRef = {
|
||||
type: "characterRef";
|
||||
ref: "first" | "second";
|
||||
};
|
||||
|
||||
/** Leaf of an expression tree */
|
||||
export type AtomicExpression = Constant | Character | CharacterRef;
|
||||
|
||||
/** Expression with binary operator */
|
||||
export type BinaryExpression = {
|
||||
type: "binary";
|
||||
opType: "+" | "-" | "*" | "/" | "%";
|
||||
lhs: Expression;
|
||||
rhs: Expression;
|
||||
};
|
||||
|
||||
/** Expression with unary operator */
|
||||
export type UnaryExpression = {
|
||||
type: "unary";
|
||||
opType: "!" | "sq" | "cube" | "sqrt" | "twice";
|
||||
operand: Expression;
|
||||
};
|
||||
|
||||
/** Root of an expression tree or subtree */
|
||||
export type Expression = AtomicExpression | BinaryExpression | UnaryExpression;
|
617
languages/shakespeare/parser/visitor.ts
Normal file
617
languages/shakespeare/parser/visitor.ts
Normal file
@ -0,0 +1,617 @@
|
||||
import { CstNode, CstNodeLocation } from "chevrotain";
|
||||
import * as C from "./cst";
|
||||
import * as V from "./visitor-types";
|
||||
import { ShakespeareParser } from "./parser";
|
||||
import { ParseError } from "../../worker-errors";
|
||||
import { DocumentRange } from "../../types";
|
||||
|
||||
const parserInstance = new ShakespeareParser();
|
||||
const BaseShakespeareVisitor = parserInstance.getBaseCstVisitorConstructor();
|
||||
|
||||
export class ShakespeareVisitor
|
||||
extends BaseShakespeareVisitor
|
||||
implements V.ShakespeareVisitorTypes
|
||||
{
|
||||
/**
|
||||
* Characters of the program currently being visited. This field is populated
|
||||
* in the `program` method, and then read by rule visitors for validating
|
||||
* character names used in the program.
|
||||
*/
|
||||
private _characters: V.Character[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// Visitor validation throws error for utility methods. There
|
||||
// doesn't seem to be another way to allow private methods.
|
||||
// this.validateVisitor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-safe wrapper around Chevrotain's `visit` function.
|
||||
* @param node CST node to visit
|
||||
* @returns Visit result of node
|
||||
*/
|
||||
private visitNode<T extends CstNode>(
|
||||
node: T
|
||||
// @ts-ignore TS complains, but does the job anyway
|
||||
): ReturnType<V.ShakespeareVisitorTypes[T["name"]]> {
|
||||
return this.visit(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert character name in source code to uniform character name
|
||||
* @param image Character name used in source code
|
||||
*/
|
||||
private toCharacterId(image: string): string {
|
||||
return image // "the \n Ghost" ...
|
||||
.split(/[\s\n]+/) // ... -> ["the", "Ghost"]
|
||||
.map((s) => s[0].toUpperCase() + s.slice(1)) // ... -> ["The", "Ghost"]
|
||||
.join(" "); // ... -> "The Ghost"
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Chevrotain location object to a DocumentRange. Note
|
||||
* that this assumes that the Chevrotain location object is
|
||||
* fully populated with no undefined fields.
|
||||
*/
|
||||
private toRange(cstLocation: CstNodeLocation): DocumentRange {
|
||||
const startLine = (cstLocation.startLine || 1) - 1;
|
||||
const endLine = cstLocation.endLine && cstLocation.endLine - 1;
|
||||
const startCol = cstLocation.startColumn && cstLocation.startColumn - 1;
|
||||
const endCol = cstLocation.endColumn; // Chevrotain `endColumn` is inclusive
|
||||
return { startLine, endLine, startCol, endCol };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates DocumentRange representing the range from the starting token to the
|
||||
* ending token, both inclusive.
|
||||
* @param start Range of the starting token
|
||||
* @param end Range of the ending token
|
||||
*/
|
||||
private joinAndGetRange(
|
||||
start: DocumentRange,
|
||||
end: DocumentRange
|
||||
): DocumentRange {
|
||||
return {
|
||||
startLine: start.startLine,
|
||||
startCol: start.startCol,
|
||||
endLine: end.endLine,
|
||||
endCol: end.endCol,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if usage of a character name is valid, ie. if the name is
|
||||
* declared at the top of the program.
|
||||
* @param character Character to check
|
||||
* @param range Location of the character usage in source code
|
||||
*/
|
||||
private validateCharacter(
|
||||
character: V.Character,
|
||||
range: DocumentRange
|
||||
): void {
|
||||
const charId = this.toCharacterId(character.name);
|
||||
if (!this._characters.find((c) => c.name === charId)) {
|
||||
throw new ParseError(
|
||||
`Character '${character.name}' is not declared`,
|
||||
range
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
program(children: C.ProgramCstChildren): V.Program {
|
||||
if (children.characterIntro == null) children.characterIntro = [];
|
||||
if (children.actSection == null) children.actSection = [];
|
||||
const chars = children.characterIntro.map((c) => this.visitNode(c));
|
||||
this._characters = chars; // this must run before rest of program is visited
|
||||
const acts = children.actSection.map((a) => this.visitNode(a));
|
||||
return { characters: chars, acts };
|
||||
}
|
||||
|
||||
programTitle(): null {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
characterIntro(children: C.CharacterIntroCstChildren): V.CharacterIntro {
|
||||
const charId = this.toCharacterId(children.Character[0].image);
|
||||
return { type: "character", name: charId };
|
||||
}
|
||||
|
||||
actHeading(children: C.ActHeadingCstChildren): V.ActHeading {
|
||||
return children.RomanNumeral[0].image.toUpperCase();
|
||||
}
|
||||
|
||||
sceneHeading(children: C.SceneHeadingCstChildren): V.SceneHeading {
|
||||
return children.RomanNumeral[0].image.toUpperCase();
|
||||
}
|
||||
|
||||
entrance(children: C.EntranceCstChildren): V.EntryExitClause {
|
||||
const chars: V.Character[] = children.Character.map((c) => ({
|
||||
type: "character",
|
||||
name: this.toCharacterId(c.image),
|
||||
}));
|
||||
const range = this.joinAndGetRange(
|
||||
this.toRange(children.SquareBracketOpen[0]),
|
||||
this.toRange(children.SquareBracketClose[0])
|
||||
);
|
||||
chars.forEach((c) => this.validateCharacter(c, range));
|
||||
return { type: "entry", characters: chars, range };
|
||||
}
|
||||
|
||||
exit(children: C.ExitCstChildren): V.EntryExitClause {
|
||||
const charId = this.toCharacterId(children.Character[0].image);
|
||||
const range = this.joinAndGetRange(
|
||||
this.toRange(children.SquareBracketOpen[0]),
|
||||
this.toRange(children.SquareBracketClose[0])
|
||||
);
|
||||
this.validateCharacter({ type: "character", name: charId }, range);
|
||||
return {
|
||||
type: "exit",
|
||||
characters: [{ type: "character", name: charId }],
|
||||
range,
|
||||
};
|
||||
}
|
||||
|
||||
multiExit(children: C.MultiExitCstChildren): V.EntryExitClause {
|
||||
const range = this.joinAndGetRange(
|
||||
this.toRange(children.SquareBracketOpen[0]),
|
||||
this.toRange(children.SquareBracketClose[0])
|
||||
);
|
||||
|
||||
// `[Exeunt]`: all characters exit
|
||||
if (children.Character == null)
|
||||
return { type: "exit", characters: null, range };
|
||||
|
||||
const chars: V.Character[] = children.Character.map((c) => ({
|
||||
type: "character",
|
||||
name: this.toCharacterId(c.image),
|
||||
}));
|
||||
chars.forEach((c) => this.validateCharacter(c, range));
|
||||
return { type: "exit", characters: chars, range };
|
||||
}
|
||||
|
||||
entryExitClause(children: C.EntryExitClauseCstChildren): V.EntryExitClause {
|
||||
if (children.entrance) return this.visitNode(children.entrance[0]);
|
||||
else if (children.exit) return this.visitNode(children.exit[0]);
|
||||
else if (children.multiExit) return this.visitNode(children.multiExit[0]);
|
||||
else throw new Error("No matched subrule.");
|
||||
}
|
||||
|
||||
actSection(children: C.ActSectionCstChildren): V.ActSection {
|
||||
if (children.sceneSection == null) children.sceneSection = [];
|
||||
const id = this.visitNode(children.actHeading[0]);
|
||||
const scenes = children.sceneSection.map((item) => this.visitNode(item));
|
||||
return { id, scenes };
|
||||
}
|
||||
|
||||
sceneSection(children: C.SceneSectionCstChildren): V.SceneSection {
|
||||
if (children.sceneSectionChunk == null) children.sceneSectionChunk = [];
|
||||
const id = this.visitNode(children.sceneHeading[0]);
|
||||
const items = children.sceneSectionChunk.flatMap<V.SceneSectionItem>(
|
||||
(item) => {
|
||||
const itemAst = this.visitNode(item);
|
||||
if (itemAst.type !== "dialogue-set") return itemAst;
|
||||
// Flatten the dialogue set into list of dialogue items
|
||||
return itemAst.lines.map((line) => ({
|
||||
type: "dialogue-item",
|
||||
speaker: itemAst.speaker,
|
||||
line,
|
||||
}));
|
||||
}
|
||||
);
|
||||
return { id, items };
|
||||
}
|
||||
|
||||
sceneSectionChunk(
|
||||
children: C.SceneSectionChunkCstChildren
|
||||
): V.SceneSectionChunk {
|
||||
if (children.dialogueSet) return this.visitNode(children.dialogueSet[0]);
|
||||
else if (children.entryExitClause)
|
||||
return this.visitNode(children.entryExitClause[0]);
|
||||
else throw new Error("No matched subrule.");
|
||||
}
|
||||
|
||||
speakerClause(children: C.SpeakerClauseCstChildren): V.SpeakerClause {
|
||||
const charId = this.toCharacterId(children.Character[0].image);
|
||||
this.validateCharacter(
|
||||
{ type: "character", name: charId },
|
||||
this.toRange(children.Character[0])
|
||||
);
|
||||
return { type: "character", name: charId };
|
||||
}
|
||||
|
||||
dialogueSet(children: C.DialogueSetCstChildren): V.DialogueSet {
|
||||
if (children.dialogueLine == null) children.dialogueLine = [];
|
||||
const speaker = this.visitNode(children.speakerClause[0]);
|
||||
const lines = children.dialogueLine.map((line) => this.visitNode(line));
|
||||
return { type: "dialogue-set", lines, speaker };
|
||||
}
|
||||
|
||||
dialogueLine(children: C.DialogueLineCstChildren): V.DialogueLine {
|
||||
if (children.conditional) return this.visitNode(children.conditional[0]);
|
||||
else if (children.nonConditionalDialogueLine)
|
||||
return this.visitNode(children.nonConditionalDialogueLine[0]);
|
||||
else throw new Error("No matched subrule.");
|
||||
}
|
||||
|
||||
nonConditionalDialogueLine(
|
||||
children: C.NonConditionalDialogueLineCstChildren
|
||||
): V.NonConditionalDialogueLine {
|
||||
if (children.assignment) return this.visitNode(children.assignment[0]);
|
||||
else if (children.stdin) return this.visitNode(children.stdin[0]);
|
||||
else if (children.stdout) return this.visitNode(children.stdout[0]);
|
||||
else if (children.goto) return this.visitNode(children.goto[0]);
|
||||
else if (children.stackPush) return this.visitNode(children.stackPush[0]);
|
||||
else if (children.stackPop) return this.visitNode(children.stackPop[0]);
|
||||
else if (children.question) return this.visitNode(children.question[0]);
|
||||
else throw new Error("No matched subrule.");
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
////////// ENGLISH //////////
|
||||
///////////////////////////////
|
||||
|
||||
noun(children: C.NounCstChildren) {
|
||||
if (children.NegativeNoun) return -1 as const;
|
||||
else if (children.NeutralNoun) return 0 as const;
|
||||
else if (children.PositiveNoun) return 1 as const;
|
||||
throw new Error("Invalid token found");
|
||||
}
|
||||
|
||||
adjective(children: C.AdjectiveCstChildren) {
|
||||
if (children.NegativeAdjective) return -1 as const;
|
||||
else if (children.NeutralAdjective) return 0 as const;
|
||||
else if (children.PositiveAdjective) return 1 as const;
|
||||
throw new Error("Invalid token found");
|
||||
}
|
||||
|
||||
possessive(children: C.PossessiveCstChildren) {
|
||||
if (children.FirstPersonPossessive) return "first" as const;
|
||||
else if (children.SecondPersonPossessive) return "second" as const;
|
||||
else if (children.ThirdPersonPossessive) return "third" as const;
|
||||
throw new Error("Invalid token found");
|
||||
}
|
||||
|
||||
reflexive(children: C.ReflexiveCstChildren) {
|
||||
if (children.FirstPersonReflexive) return "first" as const;
|
||||
else if (children.SecondPersonReflexive) return "second" as const;
|
||||
throw new Error("Invalid token found");
|
||||
}
|
||||
|
||||
comparative(children: C.ComparativeCstChildren) {
|
||||
if (children.NegativeComparative) return -1 as const;
|
||||
else if (children.PositiveComparative) return 1 as const;
|
||||
throw new Error("Invalid token found");
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
///////// STATEMENTS ////////
|
||||
///////////////////////////////
|
||||
|
||||
assignment(children: C.AssignmentCstChildren): V.AssignmentLine {
|
||||
if (children.exclaimAssignment != null)
|
||||
return this.visitNode(children.exclaimAssignment[0]);
|
||||
else if (children.arithAssignment != null)
|
||||
return this.visitNode(children.arithAssignment[0]);
|
||||
else throw new Error("No matched subrule");
|
||||
}
|
||||
|
||||
exclaimAssignment(
|
||||
children: C.ExclaimAssignmentCstChildren
|
||||
): V.AssignmentLine {
|
||||
const startRange = this.toRange(children.SecondPerson[0]);
|
||||
const endRange = this.toRange(children.SentenceMark[0]);
|
||||
return {
|
||||
type: "assign",
|
||||
value: this.visitNode(children.unarticulatedVerbalConstant[0]),
|
||||
range: this.joinAndGetRange(startRange, endRange),
|
||||
};
|
||||
}
|
||||
|
||||
arithAssignment(children: C.ArithAssignmentCstChildren): V.AssignmentLine {
|
||||
const startRange = this.toRange(children.SecondPerson[0]);
|
||||
const endRange = this.toRange(children.SentenceMark[0]);
|
||||
return {
|
||||
type: "assign",
|
||||
value: this.visitNode(children.expression[0]),
|
||||
range: this.joinAndGetRange(startRange, endRange),
|
||||
};
|
||||
}
|
||||
|
||||
stdin(children: C.StdinCstChildren): V.StdinLine {
|
||||
const startToken =
|
||||
children.Open != null ? children.Open[0] : children.Listen![0];
|
||||
const endRange = this.toRange(children.SentenceMark[0]);
|
||||
return {
|
||||
type: "stdin",
|
||||
inType: children.Heart == null ? "char" : "num",
|
||||
range: this.joinAndGetRange(this.toRange(startToken), endRange),
|
||||
};
|
||||
}
|
||||
|
||||
stdout(children: C.StdoutCstChildren): V.StdoutLine {
|
||||
const startToken =
|
||||
children.Open != null ? children.Open[0] : children.Speak![0];
|
||||
const endRange = this.toRange(children.SentenceMark[0]);
|
||||
return {
|
||||
type: "stdout",
|
||||
outType: children.Heart == null ? "char" : "num",
|
||||
range: this.joinAndGetRange(this.toRange(startToken), endRange),
|
||||
};
|
||||
}
|
||||
|
||||
goto(children: C.GotoCstChildren): V.GotoLine {
|
||||
const startToken = children.Let != null ? children.Let[0] : children.We![0];
|
||||
const endRange = this.toRange(children.SentenceMark[0]);
|
||||
return {
|
||||
type: "goto",
|
||||
targetType: children.Act == null ? "scene" : "act",
|
||||
target: children.RomanNumeral[0].image.toUpperCase(),
|
||||
range: this.joinAndGetRange(this.toRange(startToken), endRange),
|
||||
};
|
||||
}
|
||||
|
||||
stackPush(children: C.StackPushCstChildren): V.StackPushLine {
|
||||
return {
|
||||
type: "stack-push",
|
||||
expr: this.visitNode(children.expression[0]),
|
||||
range: this.joinAndGetRange(
|
||||
this.toRange(children.Remember[0]),
|
||||
this.toRange(children.SentenceMark[0])
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
stackPop(children: C.StackPopCstChildren): V.StackPopLine {
|
||||
return {
|
||||
type: "stack-pop",
|
||||
range: this.joinAndGetRange(
|
||||
this.toRange(children.Recall[0]),
|
||||
this.toRange(children.SentenceMark[0])
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
question(children: C.QuestionCstChildren): V.QuestionLine {
|
||||
const comparator = this.visitNode(children.comparator[0]);
|
||||
const lhs = this.visitNode(children.lhs[0]);
|
||||
const rhs = this.visitNode(children.rhs[0]);
|
||||
const range = this.joinAndGetRange(
|
||||
this.toRange(children.Be[0]),
|
||||
this.toRange(children.QuestionMark[0])
|
||||
);
|
||||
return { type: "question", comparator, lhs, rhs, range };
|
||||
}
|
||||
|
||||
conditional(children: C.ConditionalCstChildren): V.ConditionalLine {
|
||||
const consequent = this.visitNode(children.nonConditionalDialogueLine[0]);
|
||||
const invert = children.Not != null;
|
||||
const startRange = this.toRange(children.If[0]);
|
||||
const range = this.joinAndGetRange(startRange, consequent.range);
|
||||
return { type: "conditional", consequent, invert, range };
|
||||
}
|
||||
|
||||
comparator(children: C.ComparatorCstChildren): V.ComparisonOperator {
|
||||
const invert = children.Not != null;
|
||||
let comparator: V.ComparisonOperator = { type: "==" };
|
||||
if (children._asComparator) {
|
||||
comparator = this.visitNode(children._asComparator[0]);
|
||||
} else if (children._simpleComparator) {
|
||||
comparator = this.visitNode(children._simpleComparator[0]);
|
||||
} else if (children._moreLessComparator) {
|
||||
comparator = this.visitNode(children._moreLessComparator[0]);
|
||||
} else throw new Error("No matched subrule.");
|
||||
comparator.invert = invert;
|
||||
return comparator;
|
||||
}
|
||||
|
||||
_asComparator(): V.ComparisonOperator {
|
||||
return { type: "==" };
|
||||
}
|
||||
|
||||
_simpleComparator(
|
||||
children: C._simpleComparatorCstChildren
|
||||
): V.ComparisonOperator {
|
||||
const comperative = this.visitNode(children.comparative[0]);
|
||||
return { type: comperative === -1 ? "<" : ">" };
|
||||
}
|
||||
|
||||
_moreLessComparator(
|
||||
children: C._moreLessComparatorCstChildren
|
||||
): V.ComparisonOperator {
|
||||
// "<": more <negative-adj> OR less <positive-adj>
|
||||
// ">": more <positive-adj> OR less <negative-adj>
|
||||
const adjValue = this.visitNode(children.adjective[0]);
|
||||
if (adjValue === 0)
|
||||
throw new ParseError(
|
||||
"Cannot use neutral adjective as a comparator",
|
||||
this.toRange(children.adjective[0].location!)
|
||||
);
|
||||
|
||||
if (children.More) return { type: adjValue === -1 ? "<" : ">" };
|
||||
else if (children.Less) return { type: adjValue === -1 ? ">" : "<" };
|
||||
else throw new Error("Unexpected missing token");
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
///////// CONSTANTS /////////
|
||||
///////////////////////////////
|
||||
|
||||
constant(children: C.ConstantCstChildren): V.Constant {
|
||||
if (children._simpleConstant != null)
|
||||
return this.visitNode(children._simpleConstant[0]);
|
||||
else if (children._verbalConstant != null)
|
||||
return this.visitNode(children._verbalConstant[0]);
|
||||
throw new Error("Unexpected missing subrule");
|
||||
}
|
||||
|
||||
_simpleConstant(): V.Constant {
|
||||
return { type: "constant", value: 0 };
|
||||
}
|
||||
|
||||
_verbalConstant(children: C._verbalConstantCstChildren): V.Constant {
|
||||
return this.visitNode(children.unarticulatedVerbalConstant[0]);
|
||||
}
|
||||
|
||||
unarticulatedVerbalConstant(
|
||||
children: C.UnarticulatedVerbalConstantCstChildren
|
||||
) {
|
||||
if (children.adjective == null) children.adjective = [];
|
||||
if (children.noun == null) throw new Error("Missing noun token");
|
||||
|
||||
let nounValue = this.visitNode(children.noun[0]);
|
||||
if (nounValue === -1) {
|
||||
// Negative noun: all adjectives must be neutral or negative
|
||||
for (let i = 0; i < children.adjective.length; i++) {
|
||||
let adjectiveValue = this.visitNode(children.adjective[i]);
|
||||
if (adjectiveValue !== 0 && adjectiveValue !== -1)
|
||||
throw new ParseError(
|
||||
"Negative noun only allows negative adjectives",
|
||||
this.toRange(children.adjective[i].location!)
|
||||
);
|
||||
}
|
||||
const value = -1 * 2 ** children.adjective.length;
|
||||
return { type: "constant" as const, value };
|
||||
} else {
|
||||
// Positive noun: all adjectives must be neutral or positive
|
||||
for (let i = 0; i < children.adjective.length; i++) {
|
||||
let adjectiveValue = this.visitNode(children.adjective[i]);
|
||||
if (adjectiveValue !== 0 && adjectiveValue !== 1)
|
||||
throw new ParseError(
|
||||
"Positive noun only allows positive or neutral adjectives",
|
||||
this.toRange(children.adjective[i].location!)
|
||||
);
|
||||
}
|
||||
const value = 1 * 2 ** children.adjective.length;
|
||||
return { type: "constant" as const, value };
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
////// EXPRESSION TREE //////
|
||||
///////////////////////////////
|
||||
|
||||
expression(children: C.ExpressionCstChildren): V.Expression {
|
||||
if (children.atomicExpression != null)
|
||||
return this.visitNode(children.atomicExpression[0]);
|
||||
else if (children.sumExpression != null)
|
||||
return this.visitNode(children.sumExpression[0]);
|
||||
else if (children.differenceExpression != null)
|
||||
return this.visitNode(children.differenceExpression[0]);
|
||||
else if (children.productExpression != null)
|
||||
return this.visitNode(children.productExpression[0]);
|
||||
else if (children.quotientExpression != null)
|
||||
return this.visitNode(children.quotientExpression[0]);
|
||||
else if (children.remainderExpression != null)
|
||||
return this.visitNode(children.remainderExpression[0]);
|
||||
else if (children.factorialExpression != null)
|
||||
return this.visitNode(children.factorialExpression[0]);
|
||||
else if (children.squareExpression != null)
|
||||
return this.visitNode(children.squareExpression[0]);
|
||||
else if (children.cubeExpression != null)
|
||||
return this.visitNode(children.cubeExpression[0]);
|
||||
else if (children.squareRootExpression != null)
|
||||
return this.visitNode(children.squareRootExpression[0]);
|
||||
else if (children.twiceExpression != null)
|
||||
return this.visitNode(children.twiceExpression[0]);
|
||||
else throw new Error("No matched subrule");
|
||||
}
|
||||
|
||||
atomicExpression(
|
||||
children: C.AtomicExpressionCstChildren
|
||||
): V.AtomicExpression {
|
||||
if (children.Character != null) {
|
||||
const charId = this.toCharacterId(children.Character[0].image);
|
||||
this.validateCharacter(
|
||||
{ type: "character", name: charId },
|
||||
this.toRange(children.Character[0])
|
||||
);
|
||||
return { type: "character" as const, name: charId };
|
||||
} else if (children.FirstPerson != null) {
|
||||
return { type: "characterRef", ref: "first" };
|
||||
} else if (children.SecondPerson != null) {
|
||||
return { type: "characterRef", ref: "second" };
|
||||
} else if (children.reflexive != null) {
|
||||
const ref = this.visitNode(children.reflexive[0]);
|
||||
return { type: "characterRef", ref };
|
||||
} else if (children.constant != null) {
|
||||
return this.visitNode(children.constant[0]);
|
||||
} else throw new Error("No matched subrule");
|
||||
}
|
||||
|
||||
private visitBinaryExpression(
|
||||
code: V.BinaryExpression["opType"],
|
||||
lhs: C.ExpressionCstNode[],
|
||||
rhs: C.ExpressionCstNode[]
|
||||
): V.BinaryExpression {
|
||||
if (lhs.length !== 1 || rhs.length !== 1)
|
||||
throw new Error("Unexpected operands in binary expression");
|
||||
return {
|
||||
type: "binary",
|
||||
opType: code,
|
||||
lhs: this.visitNode(lhs[0]),
|
||||
rhs: this.visitNode(rhs[0]),
|
||||
};
|
||||
}
|
||||
|
||||
private visitUnaryExpression(
|
||||
code: V.UnaryExpression["opType"],
|
||||
expr: C.ExpressionCstNode[]
|
||||
): V.UnaryExpression {
|
||||
if (expr.length !== 1)
|
||||
throw new Error("Unexpected operands in unary expression");
|
||||
return { type: "unary", opType: code, operand: this.visitNode(expr[0]) };
|
||||
}
|
||||
|
||||
sumExpression(children: C.SumExpressionCstChildren): V.BinaryExpression {
|
||||
return this.visitBinaryExpression("+", children.lhs, children.rhs);
|
||||
}
|
||||
|
||||
differenceExpression(
|
||||
children: C.DifferenceExpressionCstChildren
|
||||
): V.BinaryExpression {
|
||||
return this.visitBinaryExpression("-", children.lhs, children.rhs);
|
||||
}
|
||||
|
||||
productExpression(
|
||||
children: C.ProductExpressionCstChildren
|
||||
): V.BinaryExpression {
|
||||
return this.visitBinaryExpression("*", children.lhs, children.rhs);
|
||||
}
|
||||
|
||||
quotientExpression(
|
||||
children: C.QuotientExpressionCstChildren
|
||||
): V.BinaryExpression {
|
||||
return this.visitBinaryExpression("/", children.lhs, children.rhs);
|
||||
}
|
||||
|
||||
remainderExpression(
|
||||
children: C.RemainderExpressionCstChildren
|
||||
): V.BinaryExpression {
|
||||
return this.visitBinaryExpression("%", children.lhs, children.rhs);
|
||||
}
|
||||
|
||||
factorialExpression(
|
||||
children: C.FactorialExpressionCstChildren
|
||||
): V.UnaryExpression {
|
||||
return this.visitUnaryExpression("!", children.expression);
|
||||
}
|
||||
|
||||
squareExpression(children: C.SquareExpressionCstChildren): V.UnaryExpression {
|
||||
return this.visitUnaryExpression("sq", children.expression);
|
||||
}
|
||||
|
||||
cubeExpression(children: C.CubeExpressionCstChildren): V.UnaryExpression {
|
||||
return this.visitUnaryExpression("cube", children.expression);
|
||||
}
|
||||
|
||||
squareRootExpression(
|
||||
children: C.SquareRootExpressionCstChildren
|
||||
): V.UnaryExpression {
|
||||
return this.visitUnaryExpression("sqrt", children.expression);
|
||||
}
|
||||
|
||||
twiceExpression(children: C.TwiceExpressionCstChildren): V.UnaryExpression {
|
||||
return this.visitUnaryExpression("twice", children.expression);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user