Add Shakespeare esolang

This commit is contained in:
Nilay Majorwar 2022-02-18 16:57:59 +05:30
parent a05731e91d
commit e810855933
26 changed files with 4106 additions and 2 deletions

View File

@ -0,0 +1,13 @@
# Shakespeare
## References
- Official documentation and implementation: http://shakespearelang.sourceforge.net
## Implementation details
- It is not necessary for questions to immediately be followed by conditionals. Conditionals
must immediately follow a question though - a runtime error is thrown otherwise.
- Empty acts and scenes are invalid. An act must contain at least one scene, and a scene must contain
at least one dialogue set or entry-exit clause.

View File

@ -0,0 +1,195 @@
import { MonacoTokensProvider } from "../types";
import * as R from "./parser/constants";
/** Runtime value of a character */
export type CharacterValue = {
value: number;
stack: number[];
};
/** Bag of characters declared in the program */
export type CharacterBag = { [name: string]: CharacterValue };
/** Type of props passed to renderer */
export type RS = {
currentSpeaker: string | null;
charactersOnStage: string[];
characterBag: CharacterBag;
questionState: boolean | null;
};
/** Sample program */
export const sampleProgram = `The Infamous Hello World Program.
Romeo, a young man with a remarkable patience.
Juliet, a likewise young woman of remarkable grace.
Ophelia, a remarkable woman much in dispute with Hamlet.
Hamlet, the flatterer of Andersen Insulting A/S.
Act I: Hamlet's insults and flattery.
Scene I: The insulting of Romeo.
[Enter Hamlet and Romeo]
Hamlet:
You lying stupid fatherless big smelly half-witted coward!
You are as stupid as the difference between a handsome rich brave
hero and thyself! Speak your mind!
You are as brave as the sum of your fat little stuffed misused dusty
old rotten codpiece and a beautiful fair warm peaceful sunny summer's day.
You are as healthy as the difference between the sum of the sweetest
reddest rose and my father and yourself! Speak your mind!
You are as cowardly as the sum of yourself and the difference
between a big mighty proud kingdom and a horse. Speak your mind.
Speak your mind!
[Exit Romeo]
Scene II: The praising of Juliet.
[Enter Juliet]
Hamlet:
Thou art as sweet as the sum of the sum of Romeo and his horse and his
black cat! Speak thy mind!
[Exit Juliet]
Scene III: The praising of Ophelia.
[Enter Ophelia]
Hamlet:
Thou art as lovely as the product of a large rural town and my amazing
bottomless embroidered purse. Speak thy mind!
Thou art as loving as the product of the bluest clearest sweetest sky
and the sum of a squirrel and a white horse. Thou art as beautiful as
the difference between Juliet and thyself. Speak thy mind!
[Exeunt Ophelia and Hamlet]
Act II: Behind Hamlet's back.
Scene I: Romeo and Juliet's conversation.
[Enter Romeo and Juliet]
Romeo:
Speak your mind. You are as worried as the sum of yourself and the
difference between my small smooth hamster and my nose. Speak your mind!
Juliet:
Speak YOUR mind! You are as bad as Hamlet! You are as small as the
difference between the square of the difference between my little pony
and your big hairy hound and the cube of your sorry little
codpiece. Speak your mind!
[Exit Romeo]
Scene II: Juliet and Ophelia's conversation.
[Enter Ophelia]
Juliet:
Thou art as good as the quotient between Romeo and the sum of a small
furry animal and a leech. Speak your mind!
Ophelia:
Thou art as disgusting as the quotient between Romeo and twice the
difference between a mistletoe and an oozing infected blister!
Speak your mind!
[Exeunt]
`;
/** Syntax highlighting */
export const editorTokensProvider: MonacoTokensProvider = {
ignoreCase: true,
tokenizer: {
/** Program title */
root: [[/[^\.]+\./, { token: "meta", next: "introduction" }]],
/** Character introductions */
introduction: [
{ include: "whitespace" },
[
`${R.CharacterRegex.source}(,\\s*)([^\\.]+\\.\\s*\\n?)`,
["tag", "", "comment"],
],
[
/(act)(\s+)([IVXLCDM]+)(:\s+)([^\.]+\.)/,
// prettier-ignore
["keyword", "", "constant", "", { token: "comment", next: "actSection" }] as any,
],
],
/** A single act of the play */
actSection: [
{ include: "whitespace" },
[/(?=act\b)/, { token: "", next: "@pop" }],
[
/(scene)(\s+)([IVXLCDM]+)(:\s+)(.+?(?:\.|\!|\?))/,
// prettier-ignore
["constant.function", "", "constant", "", {token: "comment", next: "sceneSection"}] as any,
],
],
/** A single scene of an act in the play */
sceneSection: [
{ include: "whitespace" },
[/\[/, { token: "", next: "entryExitClause" }],
[/(?=act\b)/, { token: "", next: "@pop" }],
[/(?=scene\b)/, { token: "", next: "@pop" }],
{ include: "dialogue" },
],
/** Dialogues spoken by characters */
dialogue: [
{ include: "whitespace" },
[`${R.CharacterRegex.source}:`, "tag"],
[R.CharacterRegex, "tag"],
[/nothing|zero/, "constant"],
[/(remember|recall)\b/, "keyword"],
[/(myself|i|me|thyself|yourself|thee|thou|you)\b/, "variable"],
[/speak (thine|thy|your) mind\b/, "constant.function"],
[/open (thine|thy|your) heart\b/, "constant.function"],
[/listen to (thine|thy|your) heart\b/, "constant.function"],
[/open (thine|thy|your) mind\b/, "constant.function"],
[
/(punier|smaller|worse|better|bigger|fresher|friendlier|nicer|jollier)\b/,
"attribute",
],
[/(more|less)\b/, "attribute"],
[/if (so|not),/, "keyword"],
[
/((?:let us|we shall|we must) (?:return|proceed) to (?:act|scene) )([IVXLCDM]+)/,
["annotation", "constant"],
],
[
/(sum|difference|product|quotient|remainder|factorial|square|root|cube|twice)\b/,
"operators",
],
[R.PositiveNounRegex, "constant"],
[R.NeutralNounRegex, "constant"],
[R.NegativeNounRegex, "constant"],
[R.PositiveAdjectiveRegex, "attribute"],
[R.NeutralAdjectiveRegex, "attribute"],
[R.NegativeAdjectiveRegex, "attribute"],
],
/** Clause for entry/exit of character(s) */
entryExitClause: [
{ include: "whitespace" },
[/enter|exit|exeunt/, "keyword"],
[/]/, { token: "", next: "@pop" }],
[R.CharacterRegex, "tag"],
[/and\b|,/, ""],
],
/** Utility: skip across whitespace and line breaks */
whitespace: [[/[\s\n]+/, ""]],
},
defaultToken: "",
};

View File

@ -0,0 +1,4 @@
import { setupWorker } from "../setup-worker";
import XYZLanguageEngine from "./runtime";
setupWorker(new XYZLanguageEngine());

View File

@ -0,0 +1,11 @@
import { Renderer } from "./renderer";
import { LanguageProvider } from "../types";
import { RS, sampleProgram, editorTokensProvider } from "./common";
const provider: LanguageProvider<RS> = {
Renderer,
sampleProgram,
editorTokensProvider,
};
export default provider;

View File

@ -0,0 +1,46 @@
import { RuntimeError } from "../worker-errors";
/**
* A barebones input stream implementation for consuming integers and characters from a string.
*/
export default class InputStream {
private _text: string;
/** Create a new input stream loaded with the given input */
constructor(text: string) {
this._text = text;
}
/** Remove leading whitespace from the current input stream */
private exhaustLeadingWhitespace(): void {
const firstChar = this._text.trim()[0];
const posn = this._text.search(firstChar);
this._text = this._text.slice(posn);
}
/** Parse input stream for an integer */
getNumber(): number {
this.exhaustLeadingWhitespace();
// The extra whitespace differentiates whether string is empty or all numbers.
if (this._text === "") throw new RuntimeError("Unexpected end of input");
let posn = this._text.search(/[^0-9]/);
if (posn === 0)
throw new RuntimeError(`Unexpected input character: '${this._text[0]}'`);
if (posn === -1) posn = this._text.length;
// Consume and parse numeric part
const numStr = this._text.slice(0, posn);
this._text = this._text.slice(posn);
return parseInt(numStr, 10);
}
/**
* Parse input stream for the first character, and return its ASCII code.
* If end of input, returns -1.
*/
getChar(): number {
if (this._text.length === 0) return -1;
const char = this._text[0];
this._text = this._text.slice(1);
return char.charCodeAt(0);
}
}

View 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
View 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;
}

View 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);

View 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 };
}
}

View 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);
});
}

View 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,
];

View 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;

View 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);
}
}

View File

@ -0,0 +1,24 @@
import { CharacterValue } from "../common";
import { SimpleTag } from "./utils";
type Props = {
name: string;
value: CharacterValue;
};
export const CharacterRow = (props: Props) => {
const { name, value } = props;
return (
<div style={{ margin: "20px 10px" }}>
<div>
<b style={{ marginRight: 5 }}>{name}:</b>{" "}
<pre style={{ display: "inline" }}>{value.value}</pre>
{value.stack.map((v, i) => (
<SimpleTag key={i}>{v}</SimpleTag>
))}
</div>
<div></div>
</div>
);
};

View File

@ -0,0 +1,63 @@
import { Colors } from "@blueprintjs/core";
import { RendererProps } from "../../types";
import { RS } from "../common";
import { CharacterRow } from "./character-row";
import { TopBar } from "./topbar";
/** Common border color for dark and light, using transparency */
export const BorderColor = Colors.GRAY3 + "55";
const styles = {
placeholderDiv: {
height: "100%",
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
fontSize: "1.2em",
},
rootContainer: {
height: "100%",
display: "flex",
flexDirection: "column" as "column",
},
topBarContainer: {
borderBottom: "1px solid " + BorderColor,
padding: 10,
},
mainContainer: {
flex: 1,
minHeight: 0,
overflowY: "auto" as "auto",
},
};
export const Renderer = ({ state }: RendererProps<RS>) => {
if (state == null)
return (
<div style={styles.placeholderDiv}>Run some code to see the stage!</div>
);
return (
<div style={styles.rootContainer}>
<div style={styles.topBarContainer}>
<TopBar
charactersOnStage={state.charactersOnStage}
currSpeaker={state.currentSpeaker}
questionState={state.questionState}
/>
</div>
<div style={styles.mainContainer}>
{Object.keys(state.characterBag).map((name) => (
<CharacterRow
key={name}
name={name}
value={state.characterBag[name]}
/>
))}
</div>
</div>
);
return null;
};

View File

@ -0,0 +1,56 @@
import { Tag, Text } from "@blueprintjs/core";
import { SimpleTag } from "./utils";
const styles = {
charChip: {
margin: "0 5px",
},
questionText: {
marginLeft: 30,
marginRight: 10,
},
};
type Props = {
charactersOnStage: string[];
currSpeaker: string | null;
questionState: boolean | null;
};
export const TopBar = (props: Props) => {
const { charactersOnStage, currSpeaker, questionState } = props;
const characterChips =
charactersOnStage.length === 0 ? (
<Tag large minimal>
The stage is empty
</Tag>
) : (
charactersOnStage.map((character) => {
return (
<SimpleTag
key={character}
intent={character === currSpeaker ? "active" : undefined}
>
{character}
</SimpleTag>
);
})
);
return (
<div>
{characterChips}
{questionState != null && (
<>
<Text tagName="span" style={styles.questionText}>
Answer to question:
</Text>
<SimpleTag intent={questionState ? "success" : "danger"}>
{questionState ? "yes" : "no"}
</SimpleTag>
</>
)}
</div>
);
};

View File

@ -0,0 +1,60 @@
import { Colors } from "@blueprintjs/core";
import { useDarkMode } from "../../../ui/providers/dark-mode-provider";
const backgroundColorsLight = {
success: Colors.GREEN3,
danger: Colors.RED3,
plain: Colors.GRAY3,
active: Colors.DARK_GRAY1,
};
const backgroundColorsDark = {
success: Colors.GREEN3,
danger: Colors.RED3,
plain: Colors.GRAY3,
active: Colors.LIGHT_GRAY5,
};
const foregroundColorsLight = {
success: Colors.GREEN2,
danger: Colors.RED2,
plain: Colors.DARK_GRAY1,
active: Colors.LIGHT_GRAY5,
};
const foregroundColorsDark = {
success: Colors.GREEN5,
danger: Colors.RED5,
plain: Colors.LIGHT_GRAY5,
active: Colors.DARK_GRAY1,
};
/**
* Utility component that renders a tag similar to BlueprintJS tags, but underneath
* is just a single span tag with no frills and high performance.
*/
export const SimpleTag = (props: {
children: React.ReactNode;
intent?: "success" | "danger" | "active";
}) => {
const { isDark } = useDarkMode();
const intent = props.intent == null ? "plain" : props.intent;
const backgroundMap = isDark ? backgroundColorsDark : backgroundColorsLight;
const foregroundMap = isDark ? foregroundColorsDark : foregroundColorsLight;
return (
<span
style={{
display: "inline-flex",
margin: 5,
padding: "5px 10px",
borderRadius: 3,
backgroundColor:
backgroundMap[intent] + (intent === "active" ? "aa" : "55"),
color: foregroundMap[intent],
}}
>
{props.children}
</span>
);
};

View File

@ -0,0 +1,355 @@
import { DocumentRange, LanguageEngine, StepExecutionResult } from "../types";
import { RuntimeError } from "../worker-errors";
import { CharacterBag, RS } from "./common";
import InputStream from "./input-stream";
import { Parser } from "./parser";
import * as V from "./parser/visitor-types";
/** Runtime program counter */
type PC = {
act: number; // Current act, indexed in parsing order
scene: number; // Current scene, indexed in parsing order in the current act
itemIdx: number; // Current item, indexed in parsing order in the current scene
};
/** List of character currently on stage */
type Stage = string[];
const DEFAULT_QN_RESULT: boolean | null = null;
const DEFAULT_SPEAKER: string | null = null;
const DEFAULT_STAGE = (): Stage => [];
const DEFAULT_AST = (): V.Program => ({ characters: [], acts: [] });
const DEFAULT_CHARBAG = (): CharacterBag => ({});
const DEFAULT_PC = (): PC => ({ act: 0, scene: 0, itemIdx: -1 });
export default class ShakespeareLanguageEngine implements LanguageEngine<RS> {
private _parser: Parser = new Parser();
private _charBag: CharacterBag = DEFAULT_CHARBAG();
private _ast: V.Program = DEFAULT_AST();
private _pc: PC = DEFAULT_PC();
private _stage: Stage = DEFAULT_STAGE();
private _currSpeaker: string | null = DEFAULT_SPEAKER;
private _qnResult: boolean | null = DEFAULT_QN_RESULT;
private _input: InputStream = new InputStream("");
resetState() {
this._charBag = DEFAULT_CHARBAG();
this._ast = DEFAULT_AST();
this._pc = DEFAULT_PC();
this._stage = DEFAULT_STAGE();
this._currSpeaker = DEFAULT_SPEAKER;
this._qnResult = DEFAULT_QN_RESULT;
this._input = new InputStream("");
}
validateCode(code: string) {
this._parser.parse(code);
}
prepare(code: string, input: string) {
this._ast = this._parser.parse(code);
this._input = new InputStream(input);
// Populate the character bag
for (const character of this._ast.characters)
this._charBag[character.name] = { value: 0, stack: [] };
// Set the PC to first act, first scene, first item
this._pc = { act: 0, scene: 0, itemIdx: 0 };
}
executeStep(): StepExecutionResult<RS> {
let output: string | undefined = undefined;
let finished: boolean = false;
// Execute the next step
if (this._pc.itemIdx !== -1) {
const item = this.getCurrentItem();
output = this.processSceneItem(item);
finished = this.validateAndWrapPC();
} else {
this.setCharacterBag();
this._pc.itemIdx += 1;
}
// Set the next value of current speaker and prepare
// location of next step
let nextStepLocation = null;
if (!finished) {
const item = this.getCurrentItem();
nextStepLocation = this.getItemRange(item);
if (item.type !== "dialogue-item") this._currSpeaker = null;
else this._currSpeaker = item.speaker.name;
} else this._currSpeaker = null;
// Prepare renderer state, and return
const rendererState: RS = {
characterBag: this._charBag,
charactersOnStage: this._stage,
currentSpeaker: this._currSpeaker,
questionState: this._qnResult,
};
return { rendererState, nextStepLocation, output };
}
/** Get the DocumentRange of a scene item */
private getItemRange(item: V.SceneSectionItem): DocumentRange {
if (item.type === "dialogue-item") return item.line.range;
else return item.range;
}
/** Create and set the character bag from the character intros in AST */
private setCharacterBag() {
for (const character of this._ast.characters)
this._charBag[character.name] = { value: 0, stack: [] };
}
/**
* Get item pointed to by current PC. Ensure that current PC
* points to a valid scene item.
*/
private getCurrentItem(): V.SceneSectionItem {
const currAct = this._ast.acts[this._pc.act];
const currScene = currAct.scenes[this._pc.scene];
return currScene.items[this._pc.itemIdx];
}
/** Process a single item of a scene */
private processSceneItem(item: V.SceneSectionItem): string | undefined {
if (item.type === "entry" || item.type === "exit")
this.processEntryExit(item);
else if (item.type === "dialogue-item") {
return this.processDialogueLine(item.line);
} else throw new Error("Unknown scene item type");
}
/** Process a single dialogue line */
private processDialogueLine(line: V.DialogueLine): string | undefined {
if (line.type === "conditional") return this.processConditional(line);
this._qnResult = null; // Clear question result
if (line.type === "assign") this.processAssignment(line);
else if (line.type === "stdin") this.processStdinLine(line);
else if (line.type === "stdout") return this.processStdoutLine(line);
else if (line.type === "goto") this.processGotoLine(line);
else if (line.type === "stack-push") this.processStackPush(line);
else if (line.type === "stack-pop") this.processStackPop(line);
else if (line.type === "question") this.processQuestion(line);
else throw new Error("Unknown dialogue type");
}
/** Process assignment dialogue line */
private processAssignment(line: V.AssignmentLine): void {
this.incrementPC();
const other = this.dereference("second");
const value = this.evaluateExpression(line.value);
this._charBag[other].value = value;
}
/** Process STDIN dialogue line */
private processStdinLine(line: V.StdinLine): void {
this.incrementPC();
let value = 0;
if (line.inType === "num") value = this._input.getNumber();
else value = this._input.getChar();
const other = this.dereference("second");
this._charBag[other].value = value;
}
/** Process STDOUT dialogue line */
private processStdoutLine(line: V.StdoutLine): string {
this.incrementPC();
const other = this.dereference("second");
const value = this._charBag[other].value;
if (line.outType === "num") return value.toString();
else return String.fromCharCode(value);
}
/** Process goto dialogue line and update program counter */
private processGotoLine(line: V.GotoLine): void {
if (line.targetType === "act") {
// ======= JUMP TO ACT ========
const actIdx = this._ast.acts.findIndex((act) => act.id === line.target);
if (actIdx === -1) throw new RuntimeError(`Unknown act '${line.target}'`);
this._pc = { act: actIdx, scene: 0, itemIdx: 0 };
} else {
// ======= JUMP TO SCENE ========
const actIdx = this._pc.act;
const sceneIdx = this._ast.acts[actIdx].scenes.findIndex(
(scene) => scene.id === line.target
);
if (sceneIdx === -1)
throw new RuntimeError(`Unknown scene '${line.target}'`);
this._pc = { act: actIdx, scene: sceneIdx, itemIdx: 0 };
}
}
/** Process stack push dialogue line */
private processStackPush(line: V.StackPushLine): void {
this.incrementPC();
const other = this.dereference("second");
const value = this.evaluateExpression(line.expr);
this._charBag[other].stack.push(value);
}
/** Process stack pop dialogue line */
private processStackPop(_line: V.StackPopLine): void {
this.incrementPC();
const other = this.dereference("second");
const value = this._charBag[other].stack.pop();
if (value == null)
throw new RuntimeError(`Character '${other}' has empty stack`);
this._charBag[other].value = value;
}
/** Process question dialogue line */
private processQuestion(line: V.QuestionLine): void {
this.incrementPC();
const lhsValue = this.evaluateExpression(line.lhs);
const rhsValue = this.evaluateExpression(line.rhs);
let answer = true;
const op = line.comparator.type;
if (op === "==") answer = lhsValue === rhsValue;
else if (op === "<") answer = lhsValue < rhsValue;
else if (op === ">") answer = lhsValue > rhsValue;
if (line.comparator.invert) answer = !answer;
this._qnResult = answer;
}
/** Process conditional dialogue line */
private processConditional(line: V.ConditionalLine): string | undefined {
if (this._qnResult == null)
throw new RuntimeError("Question not asked before conditional");
const answer = line.invert ? !this._qnResult : this._qnResult;
this._qnResult = null; // Clear question result
if (answer) return this.processDialogueLine(line.consequent);
else this.incrementPC();
}
/** Add or remove characters from the stage as per the clause */
private processEntryExit(clause: V.EntryExitClause): void {
this.incrementPC();
const { characters, type } = clause;
if (type === "entry") {
// ========= ENTRY CLAUSE =========
for (const char of characters!) {
if (this._stage.includes(char.name)) {
// Entry of character already on stage
throw new RuntimeError(`Character '${char.name}' already on stage`);
} else if (this._stage.length === 2) {
// Stage is full capacity
throw new RuntimeError("Too many characters on stage");
} else this._stage.push(char.name);
}
} else {
// ========= EXIT CLAUSE =========
if (characters != null) {
for (const char of characters) {
if (!this._stage.includes(char.name)) {
// Exit of character not on stage
throw new RuntimeError(`Character '${char.name}' is not on stage`);
} else this._stage.splice(this._stage.indexOf(char.name), 1);
}
} else {
// Exit of all characters
this._stage = [];
}
}
}
/**
* Increment program counter. Does not wrap to next act or scene -
* that is handled in the `executeStep` method.
*/
private incrementPC(): void {
this._pc.itemIdx += 1;
}
/**
* Check that the PC is in scene bounds. If not,
* wrap it over to the next scene or act.
* @returns True if program has ended, false otherwise
*/
private validateAndWrapPC(): boolean {
const { act, scene, itemIdx } = this._pc;
const currentScene = this._ast.acts[act].scenes[scene];
if (itemIdx >= currentScene.items.length) {
if (scene === this._ast.acts[act].scenes.length - 1) {
// Check if we're at the end of the program
if (act === this._ast.acts.length - 1) return true;
// Wrap to the next act
this._pc.act += 1;
this._pc.scene = 0;
this._pc.itemIdx = 0;
} else {
// Wrap to the next scene
this._pc.scene += 1;
this._pc.itemIdx = 0;
}
}
return false;
}
/** Dereference what character "you" or "me" refers to */
private dereference(type: "first" | "second"): string {
if (type === "first") {
// Current speaker is the required character
if (!this._currSpeaker) throw new RuntimeError("No active speaker");
else return this._currSpeaker;
} else {
// The other character is the required character
if (this._stage.length === 1)
throw new RuntimeError("Only one character on stage");
return this._stage.find((char) => char !== this._currSpeaker)!;
}
}
/**
* Evaluate the given expression and return the numeric result.
* @param expr Expression to evaluate
* @returns Numeric value of the expression
*/
private evaluateExpression(expr: V.Expression): number {
if (expr.type === "constant") return expr.value;
else if (expr.type === "character") {
// === NAMED REFERENCE TO CHARACTER ===
const { name } = expr;
if (!this._charBag.hasOwnProperty(name))
throw new RuntimeError(`Character '${name}' not found`);
return this._charBag[name].value;
} else if (expr.type === "characterRef") {
// === PRONOUN REFERENCE TO CHARACTER ===
const name = this.dereference(expr.ref);
return this._charBag[name].value;
} else if (expr.type === "binary") {
// ======= BINARY EXPRESSION =======
const { opType, lhs, rhs } = expr;
const lhsValue = this.evaluateExpression(lhs);
const rhsValue = this.evaluateExpression(rhs);
if (opType === "+") return lhsValue + rhsValue;
if (opType === "-") return lhsValue - rhsValue;
if (opType === "*") return lhsValue * rhsValue;
// For division and modulus, we need to check for division by zero
if (rhsValue === 0) throw new RuntimeError("Division by zero");
if (opType === "/") return Math.floor(lhsValue / rhsValue);
if (opType === "%") return lhsValue % rhsValue;
throw new Error(`Unknown operator '${opType}'`);
} else if (expr.type === "unary") {
// ======== UNARY EXPRESSION ========
const { opType, operand } = expr;
const operandValue = this.evaluateExpression(operand);
if (opType === "!") return this.factorial(operandValue);
if (opType === "sq") return operandValue ** 2;
if (opType === "cube") return operandValue ** 3;
if (opType === "sqrt") return Math.floor(Math.sqrt(operandValue));
if (opType === "twice") return 2 * operandValue;
throw new Error(`Unknown operator '${opType}'`);
} else throw new Error(`Unknown expression type`);
}
/** Compute the factorial of a number */
private factorial(n: number): number {
if (n < 0)
throw new RuntimeError("Cannot compute factorial of negative number");
let answer = 1;
for (let i = 1; i <= n; ++i) answer *= i;
return answer;
}
}

View File

@ -0,0 +1,89 @@
The Infamous Hello World Program.
Romeo, a young man with a remarkable patience.
Juliet, a likewise young woman of remarkable grace.
Ophelia, a remarkable woman much in dispute with Hamlet.
Hamlet, the flatterer of Andersen Insulting A/S.
Act I: Hamlet's insults and flattery.
Scene I: The insulting of Romeo.
[Enter Hamlet and Romeo]
Hamlet:
You lying stupid fatherless big smelly half-witted coward!
You are as stupid as the difference between a handsome rich brave
hero and thyself! Speak your mind!
You are as brave as the sum of your fat little stuffed misused dusty
old rotten codpiece and a beautiful fair warm peaceful sunny summer's
day. You are as healthy as the difference between the sum of the
sweetest reddest rose and my father and yourself! Speak your mind!
You are as cowardly as the sum of yourself and the difference
between a big mighty proud kingdom and a horse. Speak your mind.
Speak your mind!
[Exit Romeo]
Scene II: The praising of Juliet.
[Enter Juliet]
Hamlet:
Thou art as sweet as the sum of the sum of Romeo and his horse and his
black cat! Speak thy mind!
[Exit Juliet]
Scene III: The praising of Ophelia.
[Enter Ophelia]
Hamlet:
Thou art as lovely as the product of a large rural town and my amazing
bottomless embroidered purse. Speak thy mind!
Thou art as loving as the product of the bluest clearest sweetest sky
and the sum of a squirrel and a white horse. Thou art as beautiful as
the difference between Juliet and thyself. Speak thy mind!
[Exeunt Ophelia and Hamlet]
Act II: Behind Hamlet's back.
Scene I: Romeo and Juliet's conversation.
[Enter Romeo and Juliet]
Romeo:
Speak your mind. You are as worried as the sum of yourself and the
difference between my small smooth hamster and my nose. Speak your
mind!
Juliet:
Speak YOUR mind! You are as bad as Hamlet! You are as small as the
difference between the square of the difference between my little pony
and your big hairy hound and the cube of your sorry little
codpiece. Speak your mind!
[Exit Romeo]
Scene II: Juliet and Ophelia's conversation.
[Enter Ophelia]
Juliet:
Thou art as good as the quotient between Romeo and the sum of a small
furry animal and a leech. Speak your mind!
Ophelia:
Thou art as disgusting as the quotient between Romeo and twice the
difference between a mistletoe and an oozing infected blister! Speak
your mind!
[Exeunt]

View File

@ -0,0 +1,24 @@
import { executeProgram, readTestProgram } from "../../test-utils";
import Engine from "../runtime";
describe("Test programs", () => {
test("Hello World", async () => {
const code = readTestProgram(__dirname, "helloworld");
const result = await executeProgram(new Engine(), code);
expect(result.output).toBe("Hello World!\n");
});
test("Prime Numbers", async () => {
const code = readTestProgram(__dirname, "primes");
const result = await executeProgram(new Engine(), code, "15");
expect(result.output).toBe(">2 3 5 7 11 13 ");
});
test("Reverse cat", async () => {
const code = readTestProgram(__dirname, "reverse");
const input = "abcd efgh\nijkl mnop\n";
const expectedOutput = input.split("").reverse().join("");
const result = await executeProgram(new Engine(), code, input);
expect(result.output).toBe(expectedOutput);
});
});

View File

@ -0,0 +1,104 @@
=== Modification in Act II, Scene III, Romeo's line ===
=== Numbers separated by space instead of newline ===
Prime Number Computation in Copenhagen.
Romeo, a young man of Verona.
Juliet, a young woman.
Hamlet, a temporary variable from Denmark.
The Ghost, a limiting factor (and by a remarkable coincidence also Hamlet's father).
Act I: Interview with the other side.
Scene I: At the last hour before dawn.
[Enter the Ghost and Juliet]
The Ghost:
You pretty little warm thing! Thou art as prompt as the difference
between the square of thyself and your golden hair. Speak your mind.
Juliet:
Listen to your heart!
[Exit the Ghost]
[Enter Romeo]
Juliet:
Thou art as sweet as a sunny summer's day!
Act II: Determining divisibility.
Scene I: A private conversation.
Juliet:
Art thou more cunning than the Ghost?
Romeo:
If so, let us proceed to scene V.
[Exit Romeo]
[Enter Hamlet]
Juliet:
You are as villainous as the square root of Romeo!
Hamlet:
You are as lovely as a red rose.
Scene II: Questions and the consequences thereof.
Juliet:
Am I better than you?
Hamlet:
If so, let us proceed to scene III.
Juliet:
Is the remainder of the quotient between Romeo and me as good as
nothing?
Hamlet:
If so, let us proceed to scene IV.
Thou art as bold as the sum of thyself and a roman.
Juliet:
Let us return to scene II.
Scene III: Romeo must die!
[Exit Hamlet]
[Enter Romeo]
Juliet:
Open your heart.
[Exit Juliet]
[Enter Hamlet]
Romeo:
Thou art as rotten as the difference between nothing and a vile
disgusting snotty stinking half-witted hog. Speak your mind!
[Exit Romeo]
[Enter Juliet]
Scene IV: One small dog at a time.
[Exit Hamlet]
[Enter Romeo]
Juliet:
Thou art as handsome as the sum of thyself and my chihuahua!
Let us return to scene I.
Scene V: Fin.
[Exeunt]

View File

@ -0,0 +1,45 @@
Outputting Input Reversedly.
Othello, a stacky man.
Lady Macbeth, who pushes him around till he pops.
Act I: The one and only.
Scene I: In the beginning, there was nothing.
[Enter Othello and Lady Macbeth]
Othello:
You are nothing!
Scene II: Pushing to the very end.
Lady Macbeth:
Open your mind! Remember yourself.
Othello:
You are as hard as the sum of yourself and a stone wall. Am I as
horrid as a flirt-gill?
Lady Macbeth:
If not, let us return to scene II. Recall your imminent death!
Othello:
You are as small as the difference between yourself and a hair!
Scene III: Once you pop, you can't stop!
Lady Macbeth:
Recall your unhappy childhood. Speak your mind!
Othello:
You are as vile as the sum of yourself and a toad! Are you better
than nothing?
Lady Macbeth:
If so, let us return to scene III.
Scene IV: The end.
[Exeunt]

View File

@ -14,6 +14,7 @@
"dependencies": {
"@blueprintjs/core": "^4.0.0-beta.12",
"@monaco-editor/react": "^4.3.1",
"chevrotain": "^10.0.0",
"monaco-editor": "^0.30.1",
"next": "12.0.7",
"react": "17.0.2",
@ -30,6 +31,7 @@
"jest": "^27.4.7",
"peggy": "^1.2.0",
"ts-loader": "^9.2.6",
"ts-node": "^10.5.0",
"typescript": "4.5.2",
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1",

24
pages/ide/shakespeare.tsx Normal file
View File

@ -0,0 +1,24 @@
import React from "react";
import { NextPage } from "next";
import Head from "next/head";
import { Mainframe } from "../../ui/Mainframe";
import LangProvider from "../../languages/shakespeare";
const LANG_ID = "shakespeare";
const LANG_NAME = "Shakespeare";
const IDE: NextPage = () => {
return (
<>
<Head>
<title>{LANG_NAME} | Esolang Park</title>
</Head>
<Mainframe
langId={LANG_ID}
langName={LANG_NAME}
provider={LangProvider}
/>
</>
);
};
export default IDE;

View File

@ -14,5 +14,9 @@
{
"display": "Deadfish",
"id": "deadfish"
},
{
"display": "Shakespeare",
"id": "shakespeare"
}
]
]

132
yarn.lock
View File

@ -387,6 +387,45 @@
classnames "^2.2"
tslib "~1.13.0"
"@chevrotain/cst-dts-gen@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.0.0.tgz#2e3a35df732f2eb777bc8e0ca8e2ea211881f783"
integrity sha512-aiykxaxLAckRvINV8JtArhVtfQvbb2wr1inWdr+kPO7n6OJJgXVAVCLgb+UE1VPBlCWzPAn6JnV7BSIoZDJ3Fg==
dependencies:
"@chevrotain/gast" "^10.0.0"
"@chevrotain/types" "^10.0.0"
lodash "4.17.21"
"@chevrotain/gast@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@chevrotain/gast/-/gast-10.0.0.tgz#9c6fe9902c79d0217e2ef06488b98b61ddd103c5"
integrity sha512-R9VT8/i9br7dKsFdeNsUn897CuA4UWyBqVkK60kPA9+TEtamPTPEEiRMPDoqBTMADejqUeGzdq4CXDmioFh7tA==
dependencies:
"@chevrotain/types" "^10.0.0"
lodash "4.17.21"
"@chevrotain/types@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@chevrotain/types/-/types-10.0.0.tgz#b437da38b0dab358b4ded8cc2e36296b59bce73f"
integrity sha512-lSsFTZDX5jwXA1mTpwO92YrNZA+y7OIREZnv1qJwqOQ/bvFLVK0IX4r1Oma7OouE6cQQywxpT/+PlePGPGjoAw==
"@chevrotain/utils@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@chevrotain/utils/-/utils-10.0.0.tgz#ccff6f9d69dadcaffe49df0bcd8f30be6a8a2415"
integrity sha512-SkqgApBnHCwXyRubC5/C3s8kBDs8naC7HwrW3keArIXAPxbtYMjJXG6xXr31D5a9MXeB7MFKtXEXKH0FqF0eqQ==
"@cspotcode/source-map-consumer@0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==
"@cspotcode/source-map-support@0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5"
integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==
dependencies:
"@cspotcode/source-map-consumer" "0.8.0"
"@discoveryjs/json-ext@^0.5.0":
version "0.5.6"
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"
@ -808,6 +847,26 @@
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
"@tsconfig/node10@^1.0.7":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==
"@tsconfig/node12@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c"
integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==
"@tsconfig/node14@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2"
integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==
"@tsconfig/node16@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e"
integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14":
version "7.1.18"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8"
@ -1177,6 +1236,11 @@ acorn-walk@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
acorn@8.5.0:
version "8.5.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
@ -1268,6 +1332,11 @@ anymatch@^3.0.3, anymatch@~3.1.1, anymatch@~3.1.2:
normalize-path "^3.0.0"
picomatch "^2.0.4"
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@ -1699,6 +1768,18 @@ char-regex@^1.0.2:
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
chevrotain@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/chevrotain/-/chevrotain-10.0.0.tgz#f6807694de85a384d9bfdf05925bedb6dd7455a0"
integrity sha512-tVAJWP7syUcmWZndXWRwOtFXIEX/+ToxQHMeO/NslsVmyeZhYngEeoSosJFojvBM+EhScj3nVThP6kBf3xhfsw==
dependencies:
"@chevrotain/cst-dts-gen" "^10.0.0"
"@chevrotain/gast" "^10.0.0"
"@chevrotain/types" "^10.0.0"
"@chevrotain/utils" "^10.0.0"
lodash "4.17.21"
regexp-to-ast "0.5.0"
chokidar@3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
@ -1915,6 +1996,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@ -2089,6 +2175,11 @@ diff-sequences@^27.4.0:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5"
integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@ -3882,7 +3973,7 @@ lodash.sortby@^4.7.0:
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
lodash@^4.17.21, lodash@^4.7.0:
lodash@4.17.21, lodash@^4.17.21, lodash@^4.7.0:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -3915,6 +4006,11 @@ make-dir@^3.0.0, make-dir@^3.0.2:
dependencies:
semver "^6.0.0"
make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
makeerror@1.0.12:
version "1.0.12"
resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
@ -4740,6 +4836,11 @@ regenerator-runtime@^0.13.4:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
regexp-to-ast@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz#56c73856bee5e1fef7f73a00f1473452ab712a24"
integrity sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==
regexp.prototype.flags@^1.2.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307"
@ -5333,6 +5434,25 @@ ts-loader@^9.2.6:
micromatch "^4.0.0"
semver "^7.3.4"
ts-node@^10.5.0:
version "10.5.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.5.0.tgz#618bef5854c1fbbedf5e31465cbb224a1d524ef9"
integrity sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw==
dependencies:
"@cspotcode/source-map-support" "0.7.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
"@tsconfig/node16" "^1.0.2"
acorn "^8.4.1"
acorn-walk "^8.1.1"
arg "^4.1.0"
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
v8-compile-cache-lib "^3.0.0"
yn "3.1.1"
tsconfig-paths@^3.11.0, tsconfig-paths@^3.9.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b"
@ -5491,6 +5611,11 @@ uuid@^3.4.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
v8-compile-cache-lib@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8"
integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==
v8-compile-cache@^2.0.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
@ -5772,6 +5897,11 @@ yargs@^16.2.0:
y18n "^5.0.5"
yargs-parser "^20.2.2"
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"