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