void GenerateCode(Node node, Parser.ValueNode value) { // Push a value onto the stack switch (value.value.type) { case Value.Type.Number: Emit (node, ByteCode.PushNumber, value.value.numberValue); break; case Value.Type.String: var id = program.RegisterString (value.value.stringValue, node.name); Emit (node, ByteCode.PushString, id); break; case Value.Type.Bool: Emit (node, ByteCode.PushBool, value.value.boolValue); break; case Value.Type.Variable: Emit (node, ByteCode.PushVariable, value.value.variableName); break; case Value.Type.Null: Emit (node, ByteCode.PushNull); break; default: throw new ArgumentOutOfRangeException (); } }
void GenerateCode(Node node, Parser.AssignmentStatement statement) { // Is it a straight assignment? if (statement.operation == TokenType.EqualToOrAssign) { // Evaluate the expression, which will result in a value // on the stack GenerateCode (node, statement.valueExpression); // Stack now contains [destinationValue] } else { // It's a combined operation-plus-assignment // Get the current value of the variable Emit(node, ByteCode.PushVariable, statement.destinationVariableName); // Evaluate the expression, which will result in a value // on the stack GenerateCode (node, statement.valueExpression); // Stack now contains [currentValue, expressionValue] switch (statement.operation) { case TokenType.AddAssign: Emit (node, ByteCode.CallFunc, TokenType.Add.ToString ()); break; case TokenType.MinusAssign: Emit (node, ByteCode.CallFunc, TokenType.Minus.ToString ()); break; case TokenType.MultiplyAssign: Emit (node, ByteCode.CallFunc, TokenType.Multiply.ToString ()); break; case TokenType.DivideAssign: Emit (node, ByteCode.CallFunc, TokenType.Divide.ToString ()); break; default: throw new ArgumentOutOfRangeException (); } // Stack now contains [destinationValue] } // Store the top of the stack in the variable Emit(node, ByteCode.StoreVariable, statement.destinationVariableName); // Clean up the stack Emit (node, ByteCode.Pop); }
void GenerateCode(Node node, Parser.Expression expression) { // Expressions are either plain values, or function calls switch (expression.type) { case Parser.Expression.Type.Value: // Plain value? Emit that GenerateCode (node, expression.value); break; case Parser.Expression.Type.FunctionCall: // Evaluate all parameter expressions (which will // push them to the stack) foreach (var parameter in expression.parameters) { GenerateCode (node, parameter); } // If this function has a variable number of parameters, put // the number of parameters that were passed onto the stack if (expression.function.paramCount == -1) { Emit (node, ByteCode.PushNumber, expression.parameters.Count); } // And then call the function Emit (node, ByteCode.CallFunc, expression.function.name); break; } }
void GenerateCode(Node node, Parser.IfStatement statement) { // We'll jump to this label at the end of every clause var endOfIfStatementLabel = RegisterLabel ("endif"); foreach (var clause in statement.clauses) { var endOfClauseLabel = RegisterLabel ("skipclause"); if (clause.expression != null) { GenerateCode (node, clause.expression); Emit (node, ByteCode.JumpIfFalse, endOfClauseLabel); } GenerateCode (node, clause.statements); Emit (node, ByteCode.JumpTo, endOfIfStatementLabel); if (clause.expression != null) { Emit (node, ByteCode.Label, endOfClauseLabel); } // Clean up the stack by popping the expression that was tested earlier if (clause.expression != null) { Emit (node, ByteCode.Pop); } } Emit (node, ByteCode.Label, endOfIfStatementLabel); }
void GenerateCode(Node node, Parser.OptionStatement statement) { var destination = statement.destination; if (statement.label == null) { // this is a jump to another node Emit(node, ByteCode.RunNode, destination); } else { var stringID = program.RegisterString (statement.label, node.name); Emit (node, ByteCode.AddOption, stringID, destination); } }
void GenerateCode(Node node, Parser.ShortcutOptionGroup statement) { var endOfGroupLabel = RegisterLabel ("group_end"); var labels = new List<string> (); int optionCount = 0; foreach (var shortcutOption in statement.options) { var optionDestinationLabel = RegisterLabel ("option_" + (optionCount+1)); labels.Add (optionDestinationLabel); string endOfClauseLabel = null; if (shortcutOption.condition != null) { endOfClauseLabel = RegisterLabel ("conditional_"+optionCount); GenerateCode (node, shortcutOption.condition); Emit (node, ByteCode.JumpIfFalse, endOfClauseLabel); } var labelStringID = program.RegisterString (shortcutOption.label, node.name); Emit (node, ByteCode.AddOption, labelStringID, optionDestinationLabel); if (shortcutOption.condition != null) { Emit (node, ByteCode.Label, endOfClauseLabel); Emit (node, ByteCode.Pop); } optionCount++; } Emit (node, ByteCode.ShowOptions); if (flags.DisableShuffleOptionsAfterNextSet == true) { Emit (node, ByteCode.PushBool, false); Emit (node, ByteCode.StoreVariable, VirtualMachine.SpecialVariables.ShuffleOptions); Emit (node, ByteCode.Pop); flags.DisableShuffleOptionsAfterNextSet = false; } Emit (node, ByteCode.Jump); optionCount = 0; foreach (var shortcutOption in statement.options) { Emit (node, ByteCode.Label, labels [optionCount]); if (shortcutOption.optionNode != null) GenerateCode (node, shortcutOption.optionNode.statements); Emit (node, ByteCode.JumpTo, endOfGroupLabel); optionCount++; } // reached the end of the option group Emit (node, ByteCode.Label, endOfGroupLabel); // clean up after the jump Emit (node, ByteCode.Pop); }
void GenerateCode(Node node, IEnumerable<Yarn.Parser.Statement> statementList) { if (statementList == null) return; foreach (var statement in statementList) { GenerateCode (node, statement); } }
void GenerateCode(Node node, Parser.CustomCommand statement) { // If this command is an evaluable expression, evaluate it if (statement.expression != null) { GenerateCode (node, statement.expression); } else { switch (statement.clientCommand) { case "stop": Emit (node, ByteCode.Stop); break; case "shuffleNextOptions": // Emit code that sets "VAR_SHUFFLE_OPTIONS" to true Emit (node, ByteCode.PushBool, true); Emit (node, ByteCode.StoreVariable, VirtualMachine.SpecialVariables.ShuffleOptions); Emit (node, ByteCode.Pop); flags.DisableShuffleOptionsAfterNextSet = true; break; default: Emit (node, ByteCode.RunCommand, statement.clientCommand); break; } } }
void GenerateCode(Node node, string line) { var num = program.RegisterString (line, node.name); Emit (node, ByteCode.RunLine, num); }
// Statements void GenerateCode(Node node, Parser.Statement statement) { switch (statement.type) { case Parser.Statement.Type.CustomCommand: GenerateCode (node, statement.customCommand); break; case Parser.Statement.Type.ShortcutOptionGroup: GenerateCode (node, statement.shortcutOptionGroup); break; case Parser.Statement.Type.Block: // Blocks are just groups of statements foreach (var blockStatement in statement.block.statements) { GenerateCode(node, blockStatement); } break; case Parser.Statement.Type.IfStatement: GenerateCode (node, statement.ifStatement); break; case Parser.Statement.Type.OptionStatement: GenerateCode (node, statement.optionStatement); break; case Parser.Statement.Type.AssignmentStatement: GenerateCode (node, statement.assignmentStatement); break; case Parser.Statement.Type.Line: GenerateCode (node, statement.line); break; default: throw new ArgumentOutOfRangeException (); } }
void Emit(Node node, ByteCode code, object operandA = null, object operandB = null) { var instruction = new Instruction(); instruction.operation = code; instruction.operandA = operandA; instruction.operandB = operandB; node.instructions.Add (instruction); if (code == ByteCode.Label) { // Add this label to the label table node.labels.Add ((string)instruction.operandA, node.instructions.Count - 1); } }
internal void CompileNode(Parser.Node node) { if (program.nodes.ContainsKey(node.name)) { throw new ArgumentException ("Duplicate node name " + node.name); } var compiledNode = new Node(); compiledNode.name = node.name; var startLabel = RegisterLabel (); Emit (compiledNode, ByteCode.Label, startLabel); foreach (var statement in node.statements) { GenerateCode (compiledNode, statement); } // Does this node end after emitting AddOptions codes // without calling ShowOptions? // Note: this only works when we know that we don't have // AddOptions and then Jump up back into the code to run them. // TODO: A better solution would be for the parser to flag // whether a node has Options at the end. var hasRemainingOptions = false; foreach (var instruction in compiledNode.instructions) { if (instruction.operation == ByteCode.AddOption) { hasRemainingOptions = true; } if (instruction.operation == ByteCode.ShowOptions) { hasRemainingOptions = false; } } // If this compiled node has no lingering options to show at the end of the node, then stop at the end if (hasRemainingOptions == false) { Emit (compiledNode, ByteCode.Stop); } else { // Otherwise, show the accumulated nodes and then jump to the selected node Emit (compiledNode, ByteCode.ShowOptions); if (flags.DisableShuffleOptionsAfterNextSet == true) { Emit (compiledNode, ByteCode.PushBool, false); Emit (compiledNode, ByteCode.StoreVariable, VirtualMachine.SpecialVariables.ShuffleOptions); Emit (compiledNode, ByteCode.Pop); flags.DisableShuffleOptionsAfterNextSet = false; } Emit (compiledNode, ByteCode.RunNode); } if (node.source != null) { compiledNode.sourceTextStringID = program.RegisterString (node.source, node.name); } program.nodes [compiledNode.name] = compiledNode; }
public bool SetNode(string nodeName) { if (program.nodes.ContainsKey(nodeName) == false) { var error = "No node named " + nodeName; dialogue.LogErrorMessage(error); executionState = ExecutionState.Stopped; return false; } dialogue.LogDebugMessage ("Running node " + nodeName); // Clear the special variables dialogue.continuity.SetValue(SpecialVariables.ShuffleOptions, new Value(false)); currentNode = program.nodes [nodeName]; ResetState (); state.currentNodeName = nodeName; return true; }
internal ShortcutOption(int optionIndex, ParseNode parent, Parser p) : base(parent, p) { p.ExpectSymbol(TokenType.ShortcutOption); label = p.ExpectSymbol(TokenType.Text).value as string; // Parse the conditional ("<<if $foo>>") if it's there if (p.NextSymbolsAre(TokenType.BeginCommand, TokenType.If)) { p.ExpectSymbol(TokenType.BeginCommand); p.ExpectSymbol(TokenType.If); condition = Expression.Parse(this, p); p.ExpectSymbol(TokenType.EndCommand); } // Parse the statements belonging to this option if it has any if (p.NextSymbolIs(TokenType.Indent)) { p.ExpectSymbol(TokenType.Indent); optionNode = new Node(NodeParent().name + "." + optionIndex, this, p); p.ExpectSymbol(TokenType.Dedent); } }