示例#1
0
        /// <summary>
        /// Appends a TType to this TArgumentList. If a TArgumentList is passed, then the values contained in that
        /// TArgumentList are appended to this TArgumentList. If the values to be appended are TVariables, then the
        /// value of the TVariable will be appended instead of the TVariable itself (although if TVariable A
        /// containing TVariable B is passed, then TVariable B will be appended, as opposed to TVariable B's value).
        /// </summary>
        /// <param name="argument">The TType to append.</param>
        public void Add(TType argument)
        {
            TArgumentList argList = argument as TArgumentList;

            if (argList == null)
            {
                TVariable variable = argument as TVariable;
                if (variable == null)
                {
                    arguments.Add(argument);
                }
                else
                {
                    arguments.Add(variable.Value);
                }
            }
            else
            {
                for (int i = 0; i < argList.Count; ++i)
                {
                    arguments.Add(argList[i]);
                }
            }
        }
示例#2
0
        /// <summary>
        /// Calls the TFunction with the specified arguments.
        /// </summary>
        /// <param name="interpreter">The interpreter that the method is being called from.</param>
        /// <param name="value">
        /// The argument to pass to the function. When passing multiple arguments, use a TArgumentList.
        /// </param>
        /// <returns></returns>
        public TType Call(Interpreter interpreter, TType value)
        {
            // If value is already a TArgumentList, then simply copy the reference, otherwise create a new
            // TArgumentList and add the value to it. This TArgument list is to be passed to the function.
            TArgumentList argList = value as TArgumentList;

            if (argList == null)
            {
                argList = new TArgumentList();
                argList.Add(value);
            }

            // If the function takes a fixed number of arguments...
            if (ArgNames != null)
            {
                // Occupy the argument list with the default arguments. If there is no default argument in a place
                // where an argument should have been given, return a TException.
                if (argList.Count < ArgNames.Length)
                {
                    for (int i = argList.Count; i < DefaultArgs.Length; ++i)
                    {
                        if (DefaultArgs[i] == null)
                        {
                            break;
                        }
                        else
                        {
                            argList.Add(DefaultArgs[i]);
                        }
                    }
                }

                if (argList.Count != ArgNames.Length)
                {
                    return(new TException(interpreter, "Incorrect number of arguments for function '" + Name + "'",
                                          argList.Count.ToString() + " out of " + ArgNames.Length.ToString() + " given"));
                }
            }

            interpreter.Stack.Push();
            // Keep a track of the new stack level so that if a function calls 'exit()', which pops from the stack,
            // this will be able to be detected and the function call can be terminated properly
            int stackLevel = interpreter.Stack.Level;

            // Put the arguments on the current stack 'frame'
            if (ArgNames == null)
            {
                for (int i = 0; i < argList.Count; ++i)
                {
                    interpreter.Stack.AddVariable(new TVariable("arg" + i.ToString(), argList[i]));
                }
            }
            else
            {
                for (int i = 0; i < argList.Count; ++i)
                {
                    interpreter.Stack.AddVariable(new TVariable(ArgNames[i], argList[i]));
                }
            }

            TType returnValue = null;
            bool  dontPop     = false; // Set to true if the stack is popped during the call, i.e. if 'exit()' is called

            // Call the function
            if (HardCodedFunction != null)
            {
                returnValue = HardCodedFunction.Invoke(interpreter, argList);
            }
            else if (Block != null)
            {
                bool breakUsed;
                returnValue = Block.Execute(interpreter, out dontPop, out breakUsed);
            }
            else if (CustomFunction != "")
            {
                returnValue = interpreter.Interpret(CustomFunction, true);
                if (interpreter.Stack.Level < stackLevel)
                {
                    dontPop = true;
                }
            }

            // If returnValue is a TVariable, then return the value of the TVariable (e.g. we want to return 5, as
            // opposed to the variable X which contains 5)
            TVariable variable = returnValue as TVariable;

            if (variable != null)
            {
                returnValue = variable.Value;
            }

            if (!dontPop)
            {
                interpreter.Stack.Pop();
            }

            return(returnValue ?? TNil.Instance);
        }
 /// <summary>
 /// Adds a variable within the current Toast function's scope.
 /// </summary>
 /// <param name="variable">The TVariable to add.</param>
 public void AddVariable(TVariable variable)
 {
     stack.Peek().Add(variable.Identifier, variable);
 }
        /// <summary>
        /// Parses an assignment operation.
        /// </summary>
        /// <param name="group">The group to parse.</param>
        /// <returns>The assigned variable or function on success, otherwise a TException.</returns>
        TType ParseAssignmentGroup(Group group)
        {
            /* BNF for assignment:
             *      <assignment>       ::= 'let' ( <variable-assignment> | <function-declaration> )
             *      <var-assignment>   ::= <identifier> '=' <expression>
             *      <func-declaration> ::= <identifier> '(' { <parameters> } ')' '=' <statements>
             *      <parameters>       ::= <identifier> { ',' <identifier> }*
             *      <statements>       ::= <block> | <statement>
             *      <block>            ::= <new-line> (<statement> <new-line>)* 'end'
             * You can probably guess what <identifier>, <statement> and <new-line> are
             * The 'let' is assumed to have already been checked for
             */
            int equalsIndex = group.IndexOf("=");
            if (equalsIndex < 0) return new TException(this, "Variable or function could not be assigned a value");

            // Could be assigning a dereferenced variable
            TException exception = ParseReferencesOfGroup(group, equalsIndex);
            if (exception != null) return exception;

            if (group.Count == 1) return new TException(this, "Could not assign variable", "no variable name given");

            string variableName = group[1] as string;
            TVariable existingVariable = null;

            if (variableName == null)
            {
                exception = new TException(this, "Could not assign variable", "invalid variable name given");

                // Check if an existing variable or function is being assigned
                Group groupToParse = group[1] as Group;
                TType value = group[1] as TType;
                if (groupToParse != null) value = ParseGroup(groupToParse);
                if (value == null) return exception;

                TVariable variable = value as TVariable;
                if (variable != null) existingVariable = variable;
                else
                {
                    TFunction function = value as TFunction;
                    if (function != null) variableName = function.Name;
                    else return exception;
                }
            }

            if (group.Count == 2) return new TException(this, "Variable could not be assigned a value");
            string assignmentOperator = group[2] as string;
            if (assignmentOperator == null) // Now we assume that we're dealing with a function declaration
            {
                Group paramGroup = group[2] as Group;
                if (paramGroup == null) // The user probably just wanted to assign a variable but made a typo
                    return new TException(this, "Variable could not be assigned a value",
                        "value to assign to variable must be given");

                // Get the identifiers of all the parameters within the brackets, keeping strict watch on comma usage
                TParameterList paramList = new TParameterList();
                bool commaExpected = false;
                for (int i = 0; i < paramGroup.Count; ++i)
                {
                    if (commaExpected && (i == paramGroup.Count - 1))
                        return new TException(this, "Parameters could not be parsed", "last parameter missing");

                    string paramName = paramGroup[i] as string;

                    if (commaExpected && (paramName != ",")) paramName = null;

                    if (paramName == null) return new TException(this, "Parameters could not be parsed",
                        "invalid parameter name given");

                    if (!commaExpected) paramList.Add(paramName);
                    commaExpected = !commaExpected;
                }

                exception = new TException(this, "Function could not be given a body", "function body must be given");
                if (group.Count == 3) return exception;
                assignmentOperator = group[3] as string;
                if (assignmentOperator == null) return exception;
                else if (assignmentOperator != "=") return exception;

                TFunction function;
                if (group.Count == 4) // statement is just 'let <i><params> =', so get a block
                {
                    TBlock block = new TBlock(this, out exception, false);
                    if (exception != null) return exception;
                    function = new TFunction(variableName ?? existingVariable.Identifier, block,
                        paramList.ParameterNames, null);
                }
                else // Create a single line function
                {
                    Group funcBody = new Group(null);
                    funcBody.AddRange(group.GetRange(4, group.Count - 4));
                    function = new TFunction(variableName ?? existingVariable.Identifier, funcBody.ToString(),
                        paramList.ParameterNames, null);
                }

                exception = TFunction.AddFunction(this, function);
                if (exception != null) return exception;

                return function;
            }
            { // Assume that we're dealing with a variable assigment
                exception = new TException(this, "Variable could not be assigned a value",
                    "value to assign to variable must be given");

                if (assignmentOperator != "=") return exception;
                if (group.Count == 3) return exception;

                // Parse the expression on the right hand side of the assigment operator, and if the result is a
                // TVariable, then get it's value (we don't want to assign the TVariable itself to the new TVariable)
                Group valueGroup = new Group(null);
                valueGroup.AddRange(group.GetRange(3, group.Count - 3));
                TType value = ParseGroup(valueGroup);
                if (value is TException) return value;

                TVariable variable = value as TVariable;
                if (variable != null) value = variable.Value;

                // Make sure we don't get any circular references; they cause stack overflows when outputting them
                variable = existingVariable ?? Stack.FindVariable(variableName);
                if (value == variable) return new TException(this, "Illegal assignment attempted",
                    "variables cannot reference themselves");

                TVariable circularRefCheckVar = value as TVariable;
                while (circularRefCheckVar != null)
                {
                    TVariable variableValue = circularRefCheckVar.Value as TVariable;
                    if (variableValue != null)
                    {
                        if (variableValue == variable)
                            return new TException(this, "Illegal assignment attempted",
                                "circular reference detected");
                        else circularRefCheckVar = variableValue;
                    }
                    else circularRefCheckVar = null;
                }

                if (variable == null) // If the variable doesn't exist already, add it to the stack variable dictionary
                {
                    variable = new TVariable(variableName, value);
                    Stack.AddVariable(variable);
                }
                else variable.Value = value;

                return variable;
            }
        }
        /// <summary>
        /// Parses a group, searching for any reference and dereference operators. Operators and operands are
        /// replaced with the reference/dereferenced value.
        /// </summary>
        /// <param name="group">The group to parse.</param>
        /// <param name="limit">The index of the group to parse up to. Used by the ParseAssignmentGroup method.</param>
        /// <returns>A TException on failure, otherwise null.</returns>
        TException ParseReferencesOfGroup(Group group, int limit = -1)
        {
            int limitToUse = limit < 0 ? group.Count - 1 : limit;
            int index;

            while (true)
            {
                // Search from right to left for the first reference or dereference character
                int refIndex = group.LastIndexOf(TType.REFERENCE_CHARACTER.ToString(), limitToUse),
                    derefIndex = group.LastIndexOf(TType.DEREFERENCE_CHARACTER.ToString(), limitToUse);

                char character; // Used so we know what operation we're carrying out later
                if (refIndex < 0)
                {
                    index = derefIndex;
                    character = TType.DEREFERENCE_CHARACTER;
                }
                else if (derefIndex < 0)
                {
                    index = refIndex;
                    character = TType.REFERENCE_CHARACTER;
                }
                else
                {
                    if (refIndex > derefIndex)
                    {
                        index = refIndex;
                        character = TType.REFERENCE_CHARACTER;
                    }
                    else
                    {
                        index = derefIndex;
                        character = TType.DEREFERENCE_CHARACTER;
                    }
                }
                if (index < 0) break;

                if (index + 1 > group.Count)
                    return new TException(this, "Invalid expression term '" + character + "' at end of statement");

                // If the operand is not a variable, return a TException (can't dereference values)
                TType variable = TType.Parse(this, group[index + 1]);
                if (variable is TException) return variable as TException;
                if (!(variable is TVariable))
                {
                    if (character == TType.REFERENCE_CHARACTER)
                        return new TException(this,
                            "Attempted creation of reference to value", "expected variable identifier");
                    else
                        return new TException(this, "Attempted dereference of value", "expected variable identifier");
                }

                if (character == TType.REFERENCE_CHARACTER) group[index] = new TVariable("reference", variable);
                else
                {
                    group[index] = ((TVariable)variable).Value;
                    if (!(group[index] is TVariable) && !(group[index] is TFunction))
                        return new TException(this,
                            "Dereference of value type variable", "expected reference");
                }
                group.RemoveAt(index + 1);
                limitToUse = limit < 0 ? group.Count - 1 : limit;
            }

            return null;
        }