예제 #1
0
            internal OptionStatement(ParseNode parent, Parser p)
                : base(parent, p)
            {
                // The meaning of the string(s) we have changes
                // depending on whether we have one or two, so
                // keep them both and decide their meaning once
                // we know more

                string firstString;
                string secondString;

                // Parse "[[LABEL"
                p.ExpectSymbol(TokenType.OptionStart);
                firstString = p.ExpectSymbol(TokenType.Text).value as String;

                // If there's a | in there, get the string that comes after it
                if (p.NextSymbolIs(TokenType.OptionDelimit)) {

                    p.ExpectSymbol(TokenType.OptionDelimit);
                    secondString = p.ExpectSymbol(TokenType.Text, TokenType.Identifier).value as String;

                    // Two strings mean that the first is the label, and the second
                    // is the name of the node that we should head to if this option
                    // is selected
                    label = firstString;
                    destination = secondString;
                } else {
                    // One string means we don't have a label
                    label = null;
                    destination = firstString;
                }

                // Parse the closing ]]
                p.ExpectSymbol(TokenType.OptionEnd);
            }
예제 #2
0
        private Yarn.Value EvaluateExpression(Parser.Expression expression)
        {
            if (expression == null)
                return Yarn.Value.NULL;

            switch (expression.type) {
            case Parser.Expression.Type.Value:
                // just a regular value? return it
                return EvaluateValue (expression.value.value);

            case Parser.Expression.Type.FunctionCall:
                // get the function
                var func = expression.function;

                // evaluate all parameters
                var evaluatedParameters = new List<Value> ();

                foreach (var param in expression.parameters) {
                    var expr = EvaluateExpression (param);
                    evaluatedParameters.Add (expr);
                }

                var result = func.InvokeWithArray (evaluatedParameters.ToArray ());

                return result;

            }

            throw new NotImplementedException ("Unimplemented expression type " + expression.type.ToString ());
        }
예제 #3
0
        private IEnumerable<Dialogue.RunnerResult> RunShortcutOptionGroup(Parser.ShortcutOptionGroup shortcutOptionGroup)
        {
            var optionsToDisplay = new List<Parser.ShortcutOption> ();

            // Determine which options to present
            foreach (var option in shortcutOptionGroup.options) {
                var include = true;
                if (option.condition != null) {
                    include = EvaluateExpression(option.condition).AsBool != false;
                }
                if (include) {
                    optionsToDisplay.Add(option);
                }
            }

            if (optionsToDisplay.Count > 0) {
                // Give this list to our client
                var optionStrings = new List<string> ();

                foreach (var option in optionsToDisplay) {
                    optionStrings.Add(option.label);
                }

                Parser.ShortcutOption selectedOption = null;

                yield return new Dialogue.OptionSetResult (optionStrings, delegate(int selectedOptionIndex) {
                    selectedOption = optionsToDisplay[selectedOptionIndex];
                });

                if (selectedOption == null) {
                    dialogue.LogErrorMessage ("The OptionChooser I provided was not called before the " +
                        "next line was run! Stopping dialogue.");
                    yield break;
                }

                if (selectedOption.optionNode != null) {
                    foreach (var command in RunStatements(selectedOption.optionNode.statements)) {
                        yield return command;
                    }
                }

            }
        }
예제 #4
0
        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);
            }
        }
예제 #5
0
        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;
            }
        }
예제 #6
0
        // 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 ();
            }
        }
예제 #7
0
        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);
        }
예제 #8
0
 internal Statement(ParseNode parent, Parser p)
     : base(parent, p)
 {
     if (Block.CanParse(p)) {
         type = Type.Block;
         block = new Block(this, p);
         return;
     } else if (IfStatement.CanParse(p)) {
         type = Type.IfStatement;
         ifStatement = new IfStatement(this, p);
         return;
     } else if (OptionStatement.CanParse(p)) {
         type = Type.OptionStatement;
         optionStatement = new OptionStatement(this, p);
         return;
     } else if (AssignmentStatement.CanParse(p)) {
         type = Type.AssignmentStatement;
         assignmentStatement = new AssignmentStatement(this, p);
         return;
     } else if (ShortcutOptionGroup.CanParse(p)) {
         type = Type.ShortcutOptionGroup;
         shortcutOptionGroup = new ShortcutOptionGroup(this, p);
         return;
     } else if (CustomCommand.CanParse(p)) {
         type = Type.CustomCommand;
         customCommand = new CustomCommand(this, p);
         return;
     } else if (p.NextSymbolIs(TokenType.Text)) {
         line = p.ExpectSymbol(TokenType.Text).value as string;
         type = Type.Line;
     } else {
         throw ParseException.Make(p.tokens.Peek(), "Expected a statement here but got " + p.tokens.Peek().ToString() +" instead (was there an unbalanced if statement earlier?)");
     }
 }
예제 #9
0
            // Read a number or a variable name from the parser
            internal ValueNode(ParseNode parent, Parser p)
                : base(parent, p)
            {
                Token t = p.ExpectSymbol(TokenType.Number, TokenType.Variable, TokenType.String);

                UseToken(t);
            }
예제 #10
0
 internal ShortcutOptionGroup(ParseNode parent, Parser p)
     : base(parent, p)
 {
     // keep parsing options until we can't, but expect at least one (otherwise it's
     // not actually a list of options)
     int shortcutIndex = 1; // give each option a number so it can name itself
     do {
         _options.Add(new ShortcutOption(shortcutIndex++, this, p));
     } while (p.NextSymbolIs(TokenType.ShortcutOption));
 }
예제 #11
0
 internal static bool CanParse(Parser p)
 {
     return p.NextSymbolIs (TokenType.ShortcutOption);
 }
예제 #12
0
            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);
                }
            }
예제 #13
0
 // ParseNodes do their parsing by consuming tokens from the Parser.
 // You parse tokens into a ParseNode by using its constructor.
 internal ParseNode(ParseNode parent, Parser p)
 {
     this.parent = parent;
 }
예제 #14
0
 internal static bool CanParse(Parser p)
 {
     return p.NextSymbolIs (TokenType.OptionStart);
 }
예제 #15
0
 internal static bool CanParse(Parser p)
 {
     return p.NextSymbolsAre (TokenType.BeginCommand, TokenType.If);
 }
예제 #16
0
 internal AssignmentStatement(ParseNode parent, Parser p)
     : base(parent, p)
 {
     p.ExpectSymbol(TokenType.BeginCommand);
     p.ExpectSymbol(TokenType.Set);
     destinationVariableName = p.ExpectSymbol(TokenType.Variable).value as string;
     operation = p.ExpectSymbol(validOperators).type;
     valueExpression = Expression.Parse(this, p);
     p.ExpectSymbol(TokenType.EndCommand);
 }
예제 #17
0
        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;
        }
예제 #18
0
            internal Block(ParseNode parent, Parser p)
                : base(parent, p)
            {
                // Read the indent token
                p.ExpectSymbol(TokenType.Indent);

                // Keep reading statements until we hit a dedent
                while (p.NextSymbolIs(TokenType.Dedent) == false) {
                    // fun fact! because Blocks are a type of Statement,
                    // we get nested block parsing for free! \:D/
                    _statements.Add(new Statement(this, p));
                }

                // Tidy up by reading the dedent
                p.ExpectSymbol(TokenType.Dedent);
            }
예제 #19
0
        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;
                }
            }
        }
예제 #20
0
 internal static bool CanParse(Parser p)
 {
     return p.NextSymbolIs (TokenType.Indent);
 }
예제 #21
0
        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);
        }
예제 #22
0
            internal CustomCommand(ParseNode parent, Parser p)
                : base(parent, p)
            {
                p.ExpectSymbol(TokenType.BeginCommand);

                // Custom commands can have ANY token in them. Read them all until we hit the
                // end command token.
                var commandTokens = new List<Token>();
                do {
                    commandTokens.Add(p.ExpectSymbol());
                } while (p.NextSymbolIs(TokenType.EndCommand) == false);
                p.ExpectSymbol(TokenType.EndCommand);

                // If the first token is an identifier and the second is
                // a left paren, it may be a function call expression;
                // evaluate it as such
                if (commandTokens.Count > 1 &&
                    commandTokens[0].type == TokenType.Identifier &&
                    commandTokens[1].type == TokenType.LeftParen) {
                    var parser = new Parser(commandTokens, p.library);
                    var expression = Expression.Parse(this, parser);
                    type = Type.Expression;
                    this.expression = expression;
                } else {
                    // Otherwise, evaluate it as a command
                    type = Type.ClientCommand;

                    this.clientCommand = commandTokens[0].value;
                }
            }
예제 #23
0
        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);
        }
예제 #24
0
 internal static bool CanParse(Parser p)
 {
     return p.NextSymbolsAre (TokenType.BeginCommand, TokenType.Text) ||
         p.NextSymbolsAre (TokenType.BeginCommand, TokenType.Identifier);
 }
예제 #25
0
        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 ();
            }
        }
예제 #26
0
            internal static Expression Parse(ParseNode parent, Parser p)
            {
                // Applies Djikstra's "shunting-yard" algorithm to convert the
                // stream of infix expressions into postfix notation; we then
                // build a tree of expressions from the result

                // https://en.wikipedia.org/wiki/Shunting-yard_algorithm

                Queue<Token> _expressionRPN = new Queue<Token> ();
                var operatorStack = new Stack<Token>();

                // used for keeping count of parameters for each function
                var functionStack = new Stack<Token> ();

                var allValidTokenTypes = new List<TokenType>(Operator.operatorTypes);
                allValidTokenTypes.Add(TokenType.Number);
                allValidTokenTypes.Add(TokenType.Variable);
                allValidTokenTypes.Add(TokenType.String);
                allValidTokenTypes.Add(TokenType.LeftParen);
                allValidTokenTypes.Add(TokenType.RightParen);
                allValidTokenTypes.Add(TokenType.Identifier);
                allValidTokenTypes.Add(TokenType.Comma);
                allValidTokenTypes.Add(TokenType.True);
                allValidTokenTypes.Add(TokenType.False);
                allValidTokenTypes.Add(TokenType.Null);

                Token lastToken = null;

                // Read all the contents of the expression
                while (p.tokens.Count > 0 && p.NextSymbolIs(allValidTokenTypes.ToArray())) {

                    Token nextToken = p.ExpectSymbol(allValidTokenTypes.ToArray());

                    if (nextToken.type == TokenType.Number ||
                        nextToken.type == TokenType.Variable ||
                        nextToken.type == TokenType.String ||
                        nextToken.type == TokenType.True ||
                        nextToken.type == TokenType.False ||
                        nextToken.type == TokenType.Null) {

                        // Primitive values go straight onto the output
                        _expressionRPN.Enqueue (nextToken);
                    } else if (nextToken.type == TokenType.Identifier) {
                        operatorStack.Push (nextToken);
                        functionStack.Push (nextToken);

                        // next token must be a left paren, so process that immediately
                        nextToken = p.ExpectSymbol (TokenType.LeftParen);
                        // enter that sub-expression
                        operatorStack.Push (nextToken);

                    } else if (nextToken.type == TokenType.Comma) {

                        // Resolve this sub-expression before moving on to the
                        // next parameter
                        try {
                            // pop operators until we reach a left paren
                            while (operatorStack.Peek().type != TokenType.LeftParen) {
                                _expressionRPN.Enqueue(operatorStack.Pop());
                            }
                        } catch (InvalidOperationException) {
                            // we reached the end of the stack prematurely
                            // this means unbalanced parens!
                            throw ParseException.Make(nextToken, "Error parsing expression: " +
                                "unbalanced parentheses");
                        }

                        // We expect the top of the stack to now contain the left paren that
                        // began the list of parameters
                        if (operatorStack.Peek().type != TokenType.LeftParen) {
                            throw ParseException.Make (operatorStack.Peek (), "Expression parser got " +
                                "confused dealing with a function");
                        }

                        // The next token is not allowed to be a right-paren or a comma
                        // (that is, you can't say "foo(2,,)")
                        if (p.NextSymbolIs(TokenType.RightParen, TokenType.Comma)) {
                            throw ParseException.Make (p.tokens.Peek(), "Expected expression");
                        }

                        // Find the closest function on the stack
                        // and increment the number of parameters
                        functionStack.Peek().parameterCount++;

                    } else if (Operator.IsOperator(nextToken.type)) {
                        // This is an operator

                        // If this is a Minus, we need to determine if it's a
                        // unary minus or a binary minus.
                        // Unary minus looks like this: "-1"
                        // Binary minus looks like this: "2 - 3"
                        // Things get complex when we say stuff like "1 + -1".
                        // But it's easier when we realise that a minus
                        // is ONLY unary when the last token was a left paren,
                        // an operator, or it's the first token.

                        if (nextToken.type == TokenType.Minus) {

                            if (lastToken == null ||
                                lastToken.type == TokenType.LeftParen ||
                                Operator.IsOperator(lastToken.type)) {

                                // This is actually a unary minus.
                                nextToken.type = TokenType.UnaryMinus;
                            }
                        }

                        // We cannot assign values inside an expression. That is,
                        // saying "$foo = 2" in an express does not assign $foo to 2
                        // and then evaluate to 2. Instead, Yarn defines this
                        // to mean "$foo == 2"
                        if (nextToken.type == TokenType.EqualToOrAssign) {
                            nextToken.type = TokenType.EqualTo;
                        }

                        // O1 = this operator
                        // O2 = the token at the top of the stack
                        // While O2 is an operator, and EITHER: 1. O1 is left-associative and
                        // has precedence <= O2, or 2. O1 is right-associative and
                        // has precedence > O2:
                        while (ShouldApplyPrecedence(nextToken.type, operatorStack)) {
                            var o = operatorStack.Pop();
                            _expressionRPN.Enqueue(o);
                        }
                        operatorStack.Push(nextToken);

                    } else if (nextToken.type == TokenType.LeftParen) {

                        // Record that we have entered a paren-delimited
                        // subexpression
                        operatorStack.Push(nextToken);

                    } else if (nextToken.type == TokenType.RightParen) {

                        // We're leaving a subexpression; time to resolve the
                        // order of operations that we saw in between the parens.

                        try {
                            // pop operators until we reach a left paren
                            while (operatorStack.Peek().type != TokenType.LeftParen) {
                                _expressionRPN.Enqueue(operatorStack.Pop());
                            }
                            // pop the left paren
                            operatorStack.Pop();
                        } catch (InvalidOperationException) {
                            // we reached the end of the stack prematurely
                            // this means unbalanced parens!
                            throw ParseException.Make(nextToken, "Error parsing expression: unbalanced parentheses");
                        }

                        if (operatorStack.Peek().type == TokenType.Identifier) {
                            // This whole paren-delimited subexpression is actually
                            // a function call

                            // If the last token was a left-paren, then this
                            // was a function with no parameters; otherwise, we
                            // have an additional parameter (on top of the ones we counted
                            // while encountering commas)

                            if (lastToken.type != TokenType.LeftParen) {
                                functionStack.Peek ().parameterCount++;
                            }

                            _expressionRPN.Enqueue(operatorStack.Pop());
                            functionStack.Pop ();
                        }

                    }

                    // Record this as the last token we saw; we'll use
                    // this to figure out if minuses are unary or not
                    lastToken = nextToken;

                }

                // No more tokens; pop all operators onto the output queue
                while (operatorStack.Count > 0) {
                    _expressionRPN.Enqueue(operatorStack.Pop());
                }

                // If the output queue is empty, then this is not an expression
                if (_expressionRPN.Count == 0) {
                    throw new ParseException ("Error parsing expression: no expression found!");
                }

                // We've now got this in more easily parsed RPN form;
                // time to build the expression tree.
                Token firstToken = _expressionRPN.Peek();
                var evaluationStack = new Stack<Expression>();
                while (_expressionRPN.Count > 0) {

                    var next = _expressionRPN.Dequeue();
                    if (Operator.IsOperator(next.type)) {

                        // This is an operation

                        var info = Operator.InfoForOperator(next.type);
                        if (evaluationStack.Count < info.arguments) {
                            throw ParseException.Make(next, "Error parsing expression: not enough " +
                                "arguments for operator "+next.type.ToString());
                        }

                        var parameters = new List<Expression> ();

                        for (int i = 0; i < info.arguments; i++) {
                            parameters.Add (evaluationStack.Pop ());
                        }
                        parameters.Reverse ();

                        var operatorFunc = p.library.GetFunction (next.type.ToString());

                        var expr = new Expression (parent, operatorFunc, parameters);

                        evaluationStack.Push(expr);
                    } else if (next.type == TokenType.Identifier) {
                        // This is a function call
                        var info = p.library.GetFunction(next.value as String);

                        // Ensure that this call has the right number of params
                        if (info.IsParameterCountCorrect(next.parameterCount) == false) {
                            string error = string.Format("Error parsing expression: " +
                                "Unsupported number of parameters for function {0} (expected {1}, got {2})",
                                next.value as String,
                                info.paramCount,
                                next.parameterCount
                            );
                            throw ParseException.Make(next, error);
                        }

                        var parameterList = new List<Expression> ();
                        for (int i = 0; i < next.parameterCount; i++) {
                            parameterList.Add (evaluationStack.Pop());
                        }
                        parameterList.Reverse ();

                        var expr = new Expression (parent, info, parameterList);

                        evaluationStack.Push (expr);

                    } else {

                        // This is a raw value
                        var v = new ValueNode(parent, next);
                        Expression expr = new Expression(parent, v);
                        evaluationStack.Push(expr);

                    }
                }
                // We should now have a single expression in this stack, which is the root
                // of the expression's tree. If we have more than one, then we have a problem.
                if (evaluationStack.Count != 1) {
                    throw ParseException.Make(firstToken, "Error parsing expression " +
                        "(stack did not reduce correctly)");
                }

                // Return it
                return evaluationStack.Pop ();
            }
예제 #27
0
        // Assigns a value to a variable.
        private void RunAssignmentStatement(Parser.AssignmentStatement assignment)
        {
            // The place where we're stickin' this value.
            var variableName = assignment.destinationVariableName;

            // The value that's going into this variable.
            var computedValue = EvaluateExpression (assignment.valueExpression);

            // The current value of this variable.
            Value originalValue = dialogue.continuity.GetValue (variableName);

            // What shall we do with it?

            Value finalValue = Value.NULL;
            switch (assignment.operation) {
            case TokenType.EqualToOrAssign:
                finalValue = computedValue;
                break;
            case TokenType.AddAssign:
                finalValue = originalValue + computedValue;
                break;
            case TokenType.MinusAssign:
                finalValue = originalValue - computedValue;
                break;
            case TokenType.MultiplyAssign:
                finalValue = originalValue * computedValue;
                break;
            case TokenType.DivideAssign:
                finalValue = originalValue / computedValue;
                break;
            }

            dialogue.LogDebugMessage(string.Format("Set {0} to {1}", variableName, finalValue));
            dialogue.continuity.SetValue (variableName, finalValue);
        }
예제 #28
0
            internal IfStatement(ParseNode parent, Parser p)
                : base(parent, p)
            {
                // All if statements begin with "<<if EXPRESSION>>", so parse that
                Clause primaryClause = new Clause();

                p.ExpectSymbol(TokenType.BeginCommand);
                p.ExpectSymbol(TokenType.If);
                primaryClause.expression = Expression.Parse(this, p);
                p.ExpectSymbol(TokenType.EndCommand);

                // Read the statements for this clause until  we hit an <<endif or <<else
                // (which could be an "<<else>>" or an "<<else if"
                var statements = new List<Statement>();
                while (p.NextSymbolsAre(TokenType.BeginCommand, TokenType.EndIf) == false &&
                    p.NextSymbolsAre(TokenType.BeginCommand, TokenType.Else) == false &&
                    p.NextSymbolsAre(TokenType.BeginCommand, TokenType.ElseIf) == false) {
                    statements.Add(new Statement(this, p));

                    // Ignore any dedents
                    while (p.NextSymbolIs(TokenType.Dedent)) {
                        p.ExpectSymbol(TokenType.Dedent);
                    }
                }
                primaryClause.statements = statements;

                clauses.Add(primaryClause);

                // Handle as many <<elseif clauses as we find
                while (p.NextSymbolsAre(TokenType.BeginCommand, TokenType.ElseIf)) {
                    var elseIfClause = new Clause();

                    // Parse the syntax for this clause's condition
                    p.ExpectSymbol(TokenType.BeginCommand);
                    p.ExpectSymbol(TokenType.ElseIf);
                    elseIfClause.expression = Expression.Parse(this, p);
                    p.ExpectSymbol(TokenType.EndCommand);

                    // Read statements until we hit an <<endif, <<else or another <<elseif
                    var clauseStatements = new List<Statement>();
                    while (p.NextSymbolsAre(TokenType.BeginCommand, TokenType.EndIf) == false &&
                        p.NextSymbolsAre(TokenType.BeginCommand, TokenType.Else) == false &&
                        p.NextSymbolsAre(TokenType.BeginCommand, TokenType.ElseIf) == false) {
                        clauseStatements.Add(new Statement(this, p));

                        // Ignore any dedents
                        while (p.NextSymbolIs(TokenType.Dedent)) {
                            p.ExpectSymbol(TokenType.Dedent);
                        }

                    }

                    elseIfClause.statements = clauseStatements;

                    clauses.Add(elseIfClause);
                }

                // Handle <<else>> if we have it
                if (p.NextSymbolsAre(TokenType.BeginCommand, TokenType.Else, TokenType.EndCommand)) {

                    // parse the syntax (no expression this time, just "<<else>>"
                    p.ExpectSymbol(TokenType.BeginCommand);
                    p.ExpectSymbol(TokenType.Else);
                    p.ExpectSymbol(TokenType.EndCommand);

                    // and parse statements until we hit "<<endif"
                    var elseClause = new Clause();
                    var clauseStatements = new List<Statement>();
                    while (p.NextSymbolsAre(TokenType.BeginCommand, TokenType.EndIf) == false) {
                        clauseStatements.Add(new Statement(this, p));
                    }
                    elseClause.statements = clauseStatements;

                    this.clauses.Add(elseClause);

                    // Ignore any dedents
                    while (p.NextSymbolIs(TokenType.Dedent)) {
                        p.ExpectSymbol(TokenType.Dedent);
                    }

                }

                // Finish up by reading the <<endif>>
                p.ExpectSymbol(TokenType.BeginCommand);
                p.ExpectSymbol(TokenType.EndIf);
                p.ExpectSymbol(TokenType.EndCommand);
            }
예제 #29
0
        // Run a single statement.
        private IEnumerable<Dialogue.RunnerResult> RunStatement(Parser.Statement statement)
        {
            switch (statement.type) {

            case Parser.Statement.Type.Block:
                // Blocks just contain statements, so run them!
                foreach (var command in RunStatements (statement.block.statements)) {
                    yield return command;
                }
                break;

            case Parser.Statement.Type.Line:
                // Lines get forwarded to the client for display
                yield return new Dialogue.LineResult(statement.line);
                break;

            case Parser.Statement.Type.IfStatement:

                // Evaluate each clause in the statement, and run its statements if appropriate
                foreach (var clause in statement.ifStatement.clauses) {
                    // if this clause's expression doesn't evaluate to 0, run it; alternatively,
                    // if this clause has no expression (ie it's the 'else' clause) then also run it
                    if (clause.expression == null || EvaluateExpression(clause.expression).AsBool != false) {
                        foreach (var command in  RunStatements (clause.statements)) {
                            yield return command;
                        }
                        // don't continue on to the other clauses
                        break;
                    }
                }
                break;

            case Parser.Statement.Type.OptionStatement:
                // If we encounter an option, record it so that we can present it later
                currentOptions.Add (statement.optionStatement);
                break;

            case Parser.Statement.Type.AssignmentStatement:
                // Evaluate the expression and assign it to a variable
                RunAssignmentStatement (statement.assignmentStatement);
                break;

            case Parser.Statement.Type.ShortcutOptionGroup:
                // Evaluate and present the options, then run the stuff that came after the options
                foreach (var command in RunShortcutOptionGroup (statement.shortcutOptionGroup)) {
                    yield return command;
                }
                break;

            case Parser.Statement.Type.CustomCommand:
                // Deal with a custom command - it's either an expression or a client command
                // If it's an expression, evaluate it
                // If it's a client command, yield it to the client

                switch (statement.customCommand.type) {
                case Parser.CustomCommand.Type.Expression:
                    EvaluateExpression (statement.customCommand.expression);
                    break;
                case Parser.CustomCommand.Type.ClientCommand:
                    yield return new Dialogue.CommandResult (statement.customCommand.clientCommand);
                    break;
                }

                break;

            default:
                // Just in case we added a new type of statement and didn't implement it here
                throw new NotImplementedException ("YarnRunner: Unimplemented statement type " + statement.type);
            }
        }
예제 #30
0
 internal Operator(ParseNode parent, Parser p)
     : base(parent, p)
 {
     operatorType = p.ExpectSymbol(Operator.operatorTypes).type;
 }