UNPKG

horatio

Version:

A javascript compiler for the Shakespeare Programming Language

706 lines (498 loc) 12.9 kB
import AST from './ast'; /** * Horatio Semantics Visitor */ export default class Semantics { /** * Program */ visitProgram(program, arg) { let self = this; // comment program.comment.visit(this, null); // declarations if (program.declarations.length > 0) { program.declarations.forEach(function(declaration) { declaration.visit(self, null); }); } else { throw new Error("Semantic Error - No characters declared."); } // parts if (program.parts.length > 0) { program.parts.forEach(function(part) { part.visit(self, null); }); } else { throw new Error("Semantic Error - No parts in program."); } return null; } /** * Comment */ visitComment(comment, arg) { if (comment.sequence) { return null; } else { throw new Error("Semantic Error - Comment malformed."); } } /** * Declaration */ visitDeclaration(declaration, arg) { let c = declaration.character.visit(this, arg); if (this.characters[c.sequence]) { throw new Error("Semantic Error - Character already defined."); } else { this.characters[c.sequence] = false; } declaration.comment.visit(this, arg); return null; } /** * Character */ visitCharacter(character, arg) { let self = this; if (!character.sequence) { throw new Error("Semantic Error - Character undefined."); } if (!(character instanceof AST.Character)) { throw new Error("Semantic Error - Not of type Character."); } // Declared flag if (arg && arg.declared === true && !this.declared(character.sequence)) { throw new Error("Semantic Error - Character Undeclared"); } // Present on stage flag if (arg && arg.hasOwnProperty('on_stage')) { switch (arg.on_stage) { case true: if (!this.onStage(character.sequence)) { throw new Error("Semantic Error - Character not on stage."); } break; case false: if (this.onStage(character.sequence)) { throw new Error("Semantic Error - Character already on stage."); } break; } } return character; } /** * Part */ visitPart(part, arg) { let self = this; let n = part.numeral.visit(this, arg); part.comment.visit(this, arg); if (this.parts[n]) { throw new Error("Semantic Error - Act already defined."); } else if (part.subparts.length === 0) { throw new Error("Semantic Error - No subparts defined."); } else { this.parts[n] = []; part.subparts.forEach(function(subpart) { subpart.visit(self, {act: n}); }); } return null; } /** * Numeral */ visitNumeral(numeral, arg) { if (numeral.sequence) { return numeral.sequence; } else { throw new Error("Semantic Error - Numeral malformed."); } return null; } /** * Subparts */ visitSubpart(subpart, arg) { let n = subpart.numeral.visit(this, arg); if (this.sceneExists(arg.act, n)) { throw new Error("Semantic Error - Scene already defined."); } else { this.parts[arg.act].push(n); subpart.comment.visit(this, arg); subpart.stage.visit(this, {act: arg.act, scene: n}); } return null; } /** * Stage */ visitStage(stage, arg) { if (stage.start_presence) stage.start_presence.visit(this, arg); if (stage.dialogue) stage.dialogue.visit(this, arg); if (stage.end_presence) stage.end_presence.visit(this, arg); return null; } /** * Enter */ visitEnter(presence, arg) { if (!presence.character_1 && !presence.character_2) { throw new Error("Semantic Error - No characters entering."); } let c1 = presence.character_1.visit(this, {declared: true, on_stage: false}); this.toggleStage(c1.sequence); if (presence.character_2) { let c2 = presence.character_2.visit(this, {declared: true, on_stage: false}); if (c1.sequence === c2.sequence) { throw new Error("Semantic Error - Same character entering twice in same statement."); } this.toggleStage(c2.sequence); } return null; } /** * Exit */ visitExit(presence, arg) { if (!presence.character) { throw new Error("Semantic Error - No character exiting."); } let c = presence.character.visit(this, {declared: true, on_stage: true}); this.toggleStage(c.sequence); return null; } /** * Exeunt */ visitExeunt(presence, arg) { // - No characters on stage // x Only 1 character exeunting // x characters are the same if (presence.character_1 ? !presence.character_2 : presence.character_2) { throw new Error("Semantic Error - Either 2 or no characters can be defined, not one."); } if (presence.character_1 && presence.character_2) { let c1 = presence.character_1.visit(this, {declared: true, on_stage: true}); let c2 = presence.character_2.visit(this, {declared: true, on_stage: true}); if (c1.sequence === c2.sequence) { throw new Error("Semantic Error - Characters are the same."); } this.toggleStage(c1.sequence); this.toggleStage(c2.sequence); } else { this.exeuntStage(); } return null; } /** * Dialogue */ visitDialogue(dialogue, arg) { let self = this; dialogue.lines.forEach(function(line) { line.visit(self, arg); }); return null; } /** * Line */ visitLine(line, arg) { let self = this; let c = line.character.visit(this, {declared: true, on_stage: true}); if (line.sentences.length === 0) { throw new Error("Semantic Error - Line cannot have no sentences."); } else { arg.character = c.sequence; line.sentences.forEach(function(sentence) { sentence.visit(self, arg); }); } return null; } /** * Goto */ visitGoto(goto, arg) { let n = goto.numeral.visit(this, arg); if (!this.sceneExists(arg.act, arg.scene)) { throw new Error("Semantic Error - Scene specified by Goto does not exist in this act."); } return null; } /** * Assignment Sentence */ visitAssignmentSentence(assignment, arg) { assignment.be.visit(this, arg); assignment.value.visit(this, arg); return null; } /** * Question Sentence */ visitQuestionSentence(question, arg) { question.be.visit(this, arg); question.comparison.visit(this, arg); question.value.visit(this, arg); return null; } /** * Response Sentence */ visitResponseSentence(response, arg) { response.goto.visit(this, arg); return null; } /** * Goto Sentence */ visitGotoSentence(goto, arg) { goto.goto.visit(this, arg); return null; } /** * Integer Input Sentence */ visitIntegerInputSentence(integer_input, arg) { if (this.solo(arg.character)) { throw new Error("Semantic Error - Input calls require two characters on stage."); } return null; } /** * Char Input Sentence */ visitCharInputSentence(char_input, arg) { if (this.solo(arg.character)) { throw new Error("Semantic Error - Input calls require two characters on stage."); } return null; } /** * Integer Output Sentence */ visitIntegerOutputSentence(integer_output, arg) { if (this.solo(arg.character)) { throw new Error("Semantic Error - Output calls require two characters on stage."); } return null; } /** * Char Output Sentence */ visitCharOutputSentence(char_output, arg) { if (this.solo(arg.character)) { throw new Error("Semantic Error - Output calls require two characters on stage."); } return null; } /** * Remember Sentence */ visitRememberSentence(remember, arg) { let p = remember.pronoun.visit(this, arg); return null; } /** * Recall Sentence */ visitRecallSentence(recall, arg) { recall.comment.visit(this, arg); } /** * Positive Constant Value */ visitPositiveConstantValue(pc_val, arg) { let self = this; let n; if (!(pc_val.noun instanceof AST.PositiveNoun) && !(pc_val.noun instanceof AST.NeutralNoun)) { throw new Error("Semantic Error - Positive Constants must use a positive or neutral noun"); } else { n = pc_val.noun.visit(self, arg); } pc_val.noun.visit(this, arg); pc_val.adjectives.forEach(function(adjective) { if (!(adjective instanceof AST.PositiveAdjective) && !(adjective instanceof AST.NeutralAdjective)) { throw new Error("Semantic Error - Positive Constants must use positive of neutral adjectives."); } else { adjective.visit(self, arg); } }); //return Math.pow(2, pc_val.adjectives.length); return 0; // placeholder } /** * Negative Constant Value */ visitNegativeConstantValue(nc_val, arg) { let self = this; let n; if (!(nc_val.noun instanceof AST.NegativeNoun) && !(nc_val.noun instanceof AST.NeutralNoun)) { throw new Error("Semantic Error - Negative Constants must use a negative or neutral noun"); } else { n = nc_val.noun.visit(self, arg); } nc_val.noun.visit(this, arg); nc_val.adjectives.forEach(function(adjective) { if (!(adjective instanceof AST.NegativeAdjective) && !(adjective instanceof AST.NeutralAdjective)) { throw new Error("Semantic Error - Negative Constants must use negative of neutral adjectives."); } else { adjective.visit(self, arg); } }); //return (-1 * Math.pow(2, nc_val.adjectives.length)); return 0; // placeholder } /** * Unary Operation Value */ visitUnaryOperationValue(unary, arg) { let o = unary.operator.visit(this, arg); let v = unary.value.visit(this, arg); return 0; // placeholder } /** * Arithmetic Operation Value */ visitArithmeticOperationValue(arithmetic, arg) { let o = arithmetic.operator.visit(this, arg); let v1 = arithmetic.value_1.visit(this, arg); let v2 = arithmetic.value_2.visit(this, arg); return 0; //placeholder } /** * Pronoun Value */ visitPronounValue(pronoun, arg) { let p = pronoun.pronoun.visit(this, arg); return p; } /** * Greater Than Comparison */ visitGreaterThanComparison(comparison, arg) { let c = comparison.comparative.visit(this, arg); return c; } /** * Lesser Than Comparison */ visitLesserThanComparison(comparison, arg) { let c = comparison.comparative.visit(this, arg); return null; } /** * Equal To Comparison */ visitEqualToComparison(comparison, arg) { comparison.adjective.visit(this, arg); return null; } /** * Inverse Comparison */ visitInverseComparison(comparison, arg) { let c = comparison.comparison.visit(this, arg); return c; } /** * First Person Pronoun */ visitFirstPersonPronoun(fpp, arg) { return null; } /** * Second Person Pronoun */ visitSecondPersonPronoun(spp, arg) { return null; } /** * Positive Noun */ visitPositiveNoun(noun, arg) { return null; } /** * Neutral Noun */ visitNeutralNoun(noun, arg) { return null; } /** * Negative Noun */ visitNegativeNoun(noun, arg) { return null; } /** * Positive Adjective */ visitPositiveAdjective(adjective, arg) { return null; } /** * Neutral Adjective */ visitNeutralAdjective(adjective, arg) { return null; } /** * Negative Adjective */ visitNegativeAdjective(adjective, arg) { return null; } /** * Unary Operator */ visitUnaryOperator(operator, arg) { return null; } /** * Arithmetic Operator */ visitArithmeticOperator(operator, arg) { return null; } /** * Positive Comparative */ visitPositiveComparative(comparative, arg) { return null; } /** * Negative Comparative */ visitNegativeComparative(comparative, arg) { return null; } /** * Be */ visitBe(be, arg) { if (be.sequence==="You are" || be.sequence==="Thou art" || be.sequence==="You") { if (this.solo(arg.character)) { console.log("solo"); throw new Error("Semantic Error - Cannot assign value to interlocutor, only 1 character is on stage."); } } return null; } /** * Be Comparative */ visitBeComparative(be, arg) { if (be.sequence==="Are you" || be.sequence==="Art thou") { if (this.solo(arg.character)) throw new Error("Semantic Error - Cannot compare value of interlocutor, only 1 character is on stage."); } return null; } }