コード例 #1
0
ファイル: ScriptCompiler.cs プロジェクト: Akarias/Assembly
        /// <summary>
        /// Closes a datum.
        /// </summary>
        /// <param name="context"></param>
        public override void ExitCall(HS_Gen1Parser.CallContext context)
        {
            if (_debug)
            {
                _logger.Call(context, CompilerContextAction.Exit);
            }

            CloseDatum();
        }
コード例 #2
0
        private bool CheckParameterSanity(HS_Gen1Parser.CallContext context, FunctionInfo function)
        {
            var expressionContexts = context.expression();

            // Ignore special functions. They will be handled during the parameter push operation.
            if (!(function.Group is null))
            {
                return(true);
            }
コード例 #3
0
ファイル: ScriptCompiler.cs プロジェクト: Akarias/Assembly
        /// <summary>
        /// Processes function calls and script references. Links to a datum. Opens one or more datums. 
        /// Pops a value type. Pushes parameter types.
        /// </summary>
        /// <param name="context"></param>
        public override void EnterCall(HS_Gen1Parser.CallContext context)
        {
            if (_debug)
            {
                _logger.Call(context, CompilerContextAction.Enter);
            }

            LinkDatum();

            // Retrieve information from the context.
            string name = context.callID().GetTextSanitized();
            string expectedType = _expectedTypes.PopType();
            int contextParameterCount = context.expression().Length;

            // Handle script references.
            if (IsScriptReference(expectedType, contextParameterCount, context))
            {
                return;
            }

            // Handle functions.
            var functions = _opcodes.GetFunctionInfo(name);
            foreach(var func in functions)
            {
                if(!CheckParameterSanity(context, func))
                {
                    continue;
                }

                string returnType = DetermineReturnType(func, expectedType, context);

                // If a function, which satisfies the requirements, was found, perform the necessary push operations and create expression nodes.
                if (!(returnType is null))
                {
                    EqualityPush(func.ReturnType);
                    PushCallParameters(func, contextParameterCount, expectedType, context);
                    CreateFunctionCall(func, _opcodes.GetTypeInfo(returnType).Opcode, context.GetCorrectTextPosition(_missingCarriageReturnPositions), GetLineNumber(context));
                    return;
                }
            }

            string errorMessage = contextParameterCount == 1 ?
                    $"Failed to process the call {name} with 1 parameter." :
                    $"Failed to process the call {name} with {contextParameterCount} parameters.";
            throw new CompilerException(errorMessage + $" The expected return type was {expectedType}.", context);
        }
コード例 #4
0
ファイル: CompilerException.cs プロジェクト: Akarias/Assembly
 public CompilerException(string message, HS_Gen1Parser.CallContext context) : base(message)
 {
     SetCommonProperties(context);
     Text = context.callID().GetTextSanitized();
 }
コード例 #5
0
 public void Call(HS_Gen1Parser.CallContext context, CompilerContextAction action)
 {
     string name = context.callID().GetTextSanitized();
     WriteContextIndent("CALL", context, action, name);
 }
コード例 #6
0
 /// <summary>
 /// Exit a parse tree produced by <see cref="HS_Gen1Parser.call"/>.
 /// <para>The default implementation does nothing.</para>
 /// </summary>
 /// <param name="context">The parse tree.</param>
 public virtual void ExitCall([NotNull] HS_Gen1Parser.CallContext context)
 {
 }
コード例 #7
0
ファイル: ScriptCompiler.cs プロジェクト: Akarias/Assembly
        /// <summary>
        /// Calculates the value types for the parameters of a function and pushes them to the stack.
        /// </summary>
        /// <param name="info">The function's ScriptFunctionInfo</param>
        /// <param name="context">The call contect</param>
        /// <param name="actualParameterCount">The number of parameters which was extracted from the context</param>
        private void PushCallParameters(FunctionInfo info, int contextParameterCount, string expectedReturnType, HS_Gen1Parser.CallContext context)
        {
            int expectedParamCount = info.ParameterTypes.Count();

            // Handle regular functions.
            if (expectedParamCount > 0)
            {
                if (contextParameterCount != expectedParamCount)
                {
                    throw new CompilerException($"The function \"{context.callID().GetTextSanitized()}\" has an unexpected number of arguments. Expected: \"{expectedParamCount}\" Encountered: \"{contextParameterCount}\".", context);

                }
                _expectedTypes.PushTypes(info.ParameterTypes);
            }
            // TODO: Throw exceptions if a wrong number of parameters is detected.
            // Handle special functions.
            #region special functions
            else
            {
                bool validNumberOfArgs = false;

                switch (info.Group)
                {
                    // Any number of arguments.
                    case "Begin":
                        validNumberOfArgs = true;
                        if (info.Name.Contains("random"))
                        {
                            _expectedTypes.PushTypes(expectedReturnType, contextParameterCount);

                        }
                        else
                        {
                            // the last evaluated expression.
                            _expectedTypes.PushType(expectedReturnType);
                            _expectedTypes.PushTypes("void", contextParameterCount - 1);
                        }
                        break;

                    // Any number of arguments.
                    case "BeginCount":
                        validNumberOfArgs = true;
                        _expectedTypes.PushTypes(expectedReturnType, contextParameterCount - 1);
                        _expectedTypes.PushType("long");
                        break;

                    // 2 or 3 arguments?
                    case "If":
                        validNumberOfArgs = contextParameterCount == 2 || contextParameterCount == 3;
                        if(expectedReturnType == TypeHelper.Any)
                        {
                            _expectedTypes.PushTypes("void", contextParameterCount - 1);
                        }
                        else
                        {
                            _expectedTypes.PushTypes(expectedReturnType, contextParameterCount - 1);
                        }
                        _expectedTypes.PushType("boolean");
                        break;

                    // Cond has it's own parser rule and should be handled elsewhere.
                    case "Cond":
                        throw new CompilerException("A cond call was not recognized by the parser.", context);

                    // Two arguments.
                    case "Set":
                        validNumberOfArgs = contextParameterCount == 2;
                        _expectedTypes.PushType(TypeHelper.GlobalsReference);
                        // The second parameter will be pushed once we have determined the return type of the global.
                        _set = true;
                        break;

                    // Any number of arguments.
                    case "Logical":
                        validNumberOfArgs = contextParameterCount >= 1;
                        _expectedTypes.PushTypes("boolean", contextParameterCount);
                        break;

                    // Depends on the function. Some accept only two arguments.
                    case "Arithmetic":
                        validNumberOfArgs = contextParameterCount >= 1;
                        _expectedTypes.PushTypes("real", contextParameterCount);
                        break;

                    // TODO: Change inequality to only accept NUMBER or maybe real arguments?
                    // Two arguments.
                    case "Equality":
                    case "Inequality":
                        validNumberOfArgs = contextParameterCount == 2;
                        _expectedTypes.PushType("ANY");
                        _equality = true;
                        break;
                    
                    // One or two arguments.
                    case "Sleep":
                        validNumberOfArgs = contextParameterCount == 1 || contextParameterCount == 2;
                        if (contextParameterCount == 2)
                        {
                            _expectedTypes.PushType("script");
                        }
                        _expectedTypes.PushType("short");
                        break;
                    
                    // Zero or one argument(s).
                    case "SleepForever":
                        validNumberOfArgs = contextParameterCount == 0 || contextParameterCount == 1;
                        if (contextParameterCount == 1)
                        {
                            _expectedTypes.PushType("script");
                        }
                        break;

                    // One, two or three arguments.
                    case "SleepUntil":
                        validNumberOfArgs = contextParameterCount >= 1 || contextParameterCount <= 3;
                        if (contextParameterCount == 3)
                        {
                            _expectedTypes.PushTypes("short", "long");
                        }
                        else if(contextParameterCount == 2)
                        {
                            _expectedTypes.PushType("short");
                        }
                        _expectedTypes.PushType("boolean");
                        break;

                    // Probably two arguments.
                    case "SleepUntilGameTicks":
                        validNumberOfArgs = contextParameterCount == 2;
                        if (contextParameterCount != 2)
                        {
                            throw new CompilerException("The Compiler encountered a \"sleep_until_game_ticks\" call, which didn't have exactly two arguments.", context);
                        }
                        _expectedTypes.PushTypes("boolean", "short");
                        break;

                    // Probably one argument.
                    case "CinematicSleep":
                        validNumberOfArgs = contextParameterCount == 1;
                        if (contextParameterCount != 1)
                        {
                            throw new CompilerException("The Compiler encountered a \"sleep_until_game_ticks\" call, which didn't have exactly one argument.", context);
                        }
                        _expectedTypes.PushType("short");
                        break;

                    // One argument.
                    case "Wake":
                        validNumberOfArgs = contextParameterCount == 1;
                        _expectedTypes.PushType("script");
                        break;

                    // One argument.
                    case "Inspect":
                        validNumberOfArgs = contextParameterCount == 1;
                        _expectedTypes.PushType("ANY");
                        break;

                    // Branch has it's own parser rule and should be handled elsewhere.
                    case "Branch":
                        throw new CompilerException("A branch call was not identified by the parser.", context);
                    
                    // One argument.
                    case "ObjectCast":
                        validNumberOfArgs = contextParameterCount == 1;
                        _expectedTypes.PushType("object");
                        break;

                    // What is this?
                    case null:
                        return;

                    default:
                        throw new CompilerException($"Unimplemented function group: \"{info.Group}\".", context);
                }
                if(!validNumberOfArgs)
                {
                    throw new CompilerException($"The special function \"{info.Name}\" has an invalid number of arguments: \"{contextParameterCount}\".", context);
                }
            }
            #endregion
        }
コード例 #8
0
ファイル: ScriptCompiler.cs プロジェクト: Akarias/Assembly
        private bool IsScriptReference(string expectedReturnType, int expectedParameterCount, HS_Gen1Parser.CallContext context)
        {
            string key = context.callID().GetTextSanitized();

            if(!_scriptLookup.ContainsKey(key))
            {
                if (expectedReturnType == TypeHelper.ScriptReference)
                {
                    throw new CompilerException($"The compiler expected a Script Reference but a Script with the name \"{key}\" could not be found. " +
                        "Please check your script declarations and your spelling.", context);
                }
                else
                {
                    return false;
                }
            }

            // Try to find a script, which satisfies all conditions.
            foreach(ScriptInfo info in _scriptLookup[key])
            {
                if(info.Parameters.Count != expectedParameterCount)
                {
                    continue;
                }

                string returnType = DetermineReturnType(info, expectedReturnType, context);

                if(!(returnType is null))
                {
                    // Check for equality functions.
                    EqualityPush(returnType);

                    // Push the parameters to the type stack.
                    if (expectedParameterCount > 0)
                    {
                        string[] parameterTypes = info.Parameters.Select(p => p.ReturnType).ToArray();
                        _expectedTypes.PushTypes(parameterTypes);
                    }

                    // Create a script reference node.
                    CreateScriptReference(info, _opcodes.GetTypeInfo(returnType).Opcode, context.GetCorrectTextPosition(_missingCarriageReturnPositions), GetLineNumber(context));
                    return true;
                }
            }

            return false;
        }