/// <summary> /// Initializes a new instance of the <see cref="MethodInterpreter"/> class. /// </summary> /// <param name="baZicInterpreter">The main interpreter.</param> /// <param name="parentInterpreter">The parent interpreter.</param> /// <param name="methodDeclaration">The declaration of the method to interpret.</param> /// <param name="invokeMethod">The expression that performs the invocation.</param> /// <param name="executionFlowId">A GUID that defines in which callstack is linked.</param> internal MethodInterpreter(BaZicInterpreterCore baZicInterpreter, Interpreter parentInterpreter, MethodDeclaration methodDeclaration, InvokeMethodExpression invokeMethod, Guid executionFlowId) : base(baZicInterpreter, parentInterpreter, executionFlowId) { Requires.NotNull(methodDeclaration, nameof(methodDeclaration)); Requires.NotNull(invokeMethod, nameof(invokeMethod)); _methodDeclaration = methodDeclaration; _invokeMethod = invokeMethod; }
/// <summary> /// Add a method invocation expression to the list of method invocation in the program to analyze it later when all the declared method have been discovered. /// </summary> /// <param name="invokeMethod">The method invocation to add.</param> private void AddMethodInvocation(InvokeMethodExpression invokeMethod) { foreach (var argument in invokeMethod.Arguments) { AnalyzeExpression(argument); } _methodInvocations.Add(invokeMethod); }
/// <summary> /// Invoke a public method accessible from outside of the interpreter (EXTERN FUNCTION). /// </summary> /// <param name="executionFlowId">A GUID that defines in which callstack is linked.</param> /// <param name="methodName">The name of the method.</param> /// <param name="awaitIfAsync">Await if the method is maked as asynchronous.</param> /// <param name="args">The arguments to pass to the method.</param> /// <returns>Returns the result of the invocation (a <see cref="Task"/> in the case of a not awaited asynchronous method, or the value returned by the method).</returns> internal object InvokeMethod(Guid executionFlowId, string methodName, bool awaitIfAsync, Code.AbstractSyntaxTree.Expression[] args) { InitializeGlobalState(); BaZicInterpreter.CheckState(BaZicInterpreterState.Idle, BaZicInterpreterState.Stopped, BaZicInterpreterState.StoppedWithError); var invokeExpression = new InvokeMethodExpression(methodName, awaitIfAsync).WithParameters(args); BaZicInterpreter.ChangeState(this, new BaZicInterpreterStateChangeEventArgs(BaZicInterpreterState.Running)); return(new InvokeMethodInterpreter(BaZicInterpreter, this, invokeExpression, executionFlowId, true).Run()); }
/// <summary> /// Generates the code for an <see cref="InvokeMethodExpression"/>. /// </summary> /// <param name="expression">The expression</param> /// <returns>A BaZic code</returns> private string GenerateInvokeMethodExpression(InvokeMethodExpression expression) { Requires.NotNull(expression.MethodName, nameof(expression.MethodName)); var wait = string.Empty; var arguments = new List <string>(); foreach (var argument in expression.Arguments) { arguments.Add(GenerateExpression(argument)); } if (expression.Await) { wait = "AWAIT "; } return($"{wait}{expression.MethodName}({string.Join(", ", arguments)})"); }
/// <summary> /// Analyze a method invocation. /// </summary> /// <param name="invokeMethod">The expression to analyze.</param> private void ValidateMethodInvocation(InvokeMethodExpression invokeMethod) { var methodDeclaration = _declaredMethods.LastOrDefault(method => string.Compare(method.Name.Identifier, invokeMethod.MethodName.Identifier, StringComparison.Ordinal) == 0); // The name is case sensitive. if (methodDeclaration == null) { AddIssue(invokeMethod, BaZicParserExceptionLevel.Error, L.BaZic.Optimizer.FormattedUndeclaredName(invokeMethod.MethodName)); } else { if (invokeMethod.Await) { if (!methodDeclaration.IsAsync) { AddIssue(invokeMethod, BaZicParserExceptionLevel.Error, L.BaZic.Parser.CannotAwait); } } if (methodDeclaration.Arguments.Count != invokeMethod.Arguments.Count) { AddIssue(invokeMethod, BaZicParserExceptionLevel.Error, L.BaZic.Optimizer.FormattedMethodNoMatchArguments(invokeMethod.MethodName, invokeMethod.Arguments.Count)); } } }
/// <summary> /// Parse a part of an expression that can be a reference or primary value followed by an accesser like array indexer or method invocation. /// /// Corresponding grammar : /// Primary_Expression_Start Bracket_Expression* ((Member_Access | Method_Invocation) Bracket_Expression* )* /// </summary> /// <param name="isRequired">Defines whether it is required/expected to parse an expression. If true, throw an exception if no expression is parsed.</param> /// <returns>Returns an expression.</returns> private Expression ParsePrimaryExpression(bool isRequired) { Expression[] bracketExpression = null; var expressionLine = CurrentToken.Line; var expressionColumn = CurrentToken.Column; var expressionStartOffset = CurrentToken.StartOffset; var expressionParsedLength = CurrentToken.ParsedLength; // Primary_Expression_Start var expression = ParsePrimaryExpressionStart(isRequired); // Bracket_Expression * do { var bracketToken = CurrentToken; bracketExpression = ParseBracketExpression(); if (bracketExpression != null) { var referenceExpression = expression as ReferenceExpression; if (referenceExpression == null) { AddIssue(new BaZicParserException(expressionLine, expressionColumn, expressionStartOffset, expressionParsedLength, L.BaZic.Parser.Expressions.UnexpectedIndexer)); } if (bracketExpression.Length == 0) { AddIssue(new BaZicParserException(bracketToken.Line, bracketToken.Column, expressionStartOffset, expressionParsedLength, L.BaZic.Parser.Expressions.IndexerExpected)); return(null); } var arrayIndexer = new ArrayIndexerExpression(referenceExpression, bracketExpression) { Line = bracketToken.Line, Column = bracketToken.Column, StartOffset = bracketToken.StartOffset, NodeLength = bracketToken.ParsedLength }; ValidateArrayIndexerExpression(arrayIndexer); expression = arrayIndexer; } } while (bracketExpression != null); // ((Member_Access | Method_Invocation) Bracket_Expression* )* while (CurrentToken.TokenType == TokenType.Dot || CurrentToken.TokenType == TokenType.LeftParenth) { if (CurrentToken.TokenType == TokenType.Dot) { // Member_Access var memberNameToken = CurrentToken; var memberAccess = ParseMemberAccessPart(true); if (!string.IsNullOrEmpty(memberAccess)) { var referenceExpression = expression as ReferenceExpression; if (referenceExpression == null) { AddIssue(new BaZicParserException(expressionLine, expressionColumn, expressionStartOffset, expressionParsedLength, L.BaZic.Parser.Expressions.IllegalPropertyAccess)); } expression = new PropertyReferenceExpression(referenceExpression, memberAccess) { Line = memberNameToken.Line, Column = memberNameToken.Column + 1, // +1 because we don't want to show a potential error on the dot. StartOffset = memberNameToken.StartOffset + 1, // +1 because we don't want to show a potential error on the dot. NodeLength = memberNameToken.ParsedLength }; } } else if (CurrentToken.TokenType == TokenType.LeftParenth) { // Method_Invocation var methodInvocationParameters = ParseMethodInvocation(); var propertyReferenceExpression = expression as PropertyReferenceExpression; if (expression is VariableReferenceExpression variableReferenceExpression) { var methodInvoke = new InvokeMethodExpression(variableReferenceExpression.Name.ToString(), false) { Line = variableReferenceExpression.Line, Column = variableReferenceExpression.Column, StartOffset = variableReferenceExpression.StartOffset, NodeLength = variableReferenceExpression.NodeLength } .WithParameters(methodInvocationParameters); AddMethodInvocation(methodInvoke); expression = methodInvoke; } else if (propertyReferenceExpression != null) { var methodInvoke = new InvokeCoreMethodExpression(propertyReferenceExpression.TargetObject, propertyReferenceExpression.PropertyName.ToString(), false) { Line = propertyReferenceExpression.Line, Column = propertyReferenceExpression.Column, StartOffset = propertyReferenceExpression.StartOffset, NodeLength = propertyReferenceExpression.NodeLength }; methodInvoke.WithParameters(methodInvocationParameters); ValidateCoreMethodInvocation(methodInvoke); expression = methodInvoke; } else { AddIssue(new BaZicParserException(expressionLine, expressionColumn, expressionStartOffset, expressionParsedLength, L.BaZic.Parser.Expressions.MethodNameExpected)); } } else { AddIssue(new BaZicParserException(expressionLine, expressionColumn, expressionStartOffset, expressionParsedLength, L.BaZic.Parser.Expressions.MethodNameExpected)); } // Bracket_Expression* do { var bracketToken = CurrentToken; bracketExpression = ParseBracketExpression(); if (bracketExpression != null) { var referenceExpression = expression as ReferenceExpression; if (referenceExpression == null) { AddIssue(new BaZicParserException(expressionLine, expressionColumn, expressionStartOffset, expressionParsedLength, L.BaZic.Parser.Expressions.UnexpectedIndexer)); } if (bracketExpression.Length == 0) { AddIssue(new BaZicParserException(bracketToken.Line, bracketToken.Column, expressionStartOffset, expressionParsedLength, L.BaZic.Parser.Expressions.IndexerExpected)); } var arrayIndexer = new ArrayIndexerExpression(referenceExpression, bracketExpression) { Line = bracketToken.Line, Column = bracketToken.Column, StartOffset = bracketToken.StartOffset, NodeLength = bracketToken.ParsedLength }; ValidateArrayIndexerExpression(arrayIndexer); expression = arrayIndexer; } } while (bracketExpression != null); } return(expression); }
/// <summary> /// Inline, if possible a local method invocation. /// </summary> /// <param name="invokeMethod">The method invocation expression.</param> /// <param name="returnedValueAssignation">The expression that must receive the invocation result.</param> /// <returns>A set of statements that represents the inlined method.</returns> private List <Statement> InlineMethodInvocation(InvokeMethodExpression invokeMethod, Expression returnedValueAssignation) { var methodDeclaration = _program.Methods.SingleOrDefault(method => string.Compare(method.Name.Identifier, invokeMethod.MethodName.Identifier, StringComparison.Ordinal) == 0); if (methodDeclaration == null) { throw new BaZicParserException(invokeMethod.Line, invokeMethod.Column, invokeMethod.StartOffset, invokeMethod.NodeLength, L.BaZic.Optimizer.FormattedUndeclaredName(invokeMethod.MethodName)); } else if (invokeMethod.Arguments.Count != methodDeclaration.Arguments.Count) { throw new BaZicParserException(invokeMethod.Line, invokeMethod.Column, invokeMethod.StartOffset, invokeMethod.NodeLength, L.BaZic.Optimizer.FormattedMethodNoMatchArguments(invokeMethod.MethodName, invokeMethod.Arguments.Count)); } else if (methodDeclaration.IsAsync) { return(null); } else if (_methodInformations.Count(method => method.MethodDeclaration.Id == methodDeclaration.Id) >= Consts.RecursivityLimit) { // After a certain number of recursive call, we stop to perform a recursive call. return(null); } var newStatements = new List <Statement>(); var startMethod = CreateNewLabel(); var endMethod = CreateNewLabel(); var arguments = new Dictionary <Guid, VariableDeclaration>(); for (var i = 0; i < methodDeclaration.Arguments.Count; i++) { var argument = methodDeclaration.Arguments[i]; arguments.Add(argument.Id, new VariableDeclaration(argument.Name.Identifier, argument.IsArray).WithDefaultValue(OptimizeExpression(invokeMethod.Arguments[i]))); } var methodInfo = new MethodInformation() { MethodDeclaration = methodDeclaration, SubstituteVariableDeclarations = arguments, StartLabel = startMethod, EndLabel = endMethod, ReturnValueRecepter = new VariableDeclaration($"RET{startMethod.Name}", false) }; _methodInformations.Insert(0, methodInfo); foreach (var substituteArgument in methodInfo.SubstituteVariableDeclarations) { newStatements.Add(substituteArgument.Value); } newStatements.Add(methodInfo.ReturnValueRecepter); newStatements.Add(startMethod); newStatements.AddRange(OptimizeStatementBlock(methodDeclaration.Statements)); newStatements.Add(endMethod); if (returnedValueAssignation != null) { newStatements.Add(new AssignStatement(OptimizeExpression(returnedValueAssignation), new VariableReferenceExpression(methodInfo.ReturnValueRecepter.Name.Identifier) { VariableDeclarationID = methodInfo.ReturnValueRecepter.Id })); } _methodInformations.RemoveAt(0); return(newStatements); }
internal void Start(object[] args) { BaZicInterpreter.CheckState(BaZicInterpreterState.Preparing); InitializeGlobalState(); var entryPoint = GetEntryPointMethod(); if (IsAborted) { return; } if (BaZicInterpreter.Verbose) { VerboseLog(L.BaZic.Runtime.Interpreters.ProgramInterpreter.EntryPointDetected); } BaZicInterpreter.ChangeState(this, new BaZicInterpreterStateChangeEventArgs(BaZicInterpreterState.Running)); var argsExpressions = new List <Code.AbstractSyntaxTree.Expression>(); if (args != null) { foreach (var argument in args) { argsExpressions.Add(new PrimitiveExpression(argument)); } } var entryPointInvocation = new InvokeMethodExpression(Consts.EntryPointMethodName, false).WithParameters(new ArrayCreationExpression().WithValues(argsExpressions.ToArray())); var entryPointInterpreter = new MethodInterpreter(BaZicInterpreter, this, entryPoint, entryPointInvocation, ExecutionFlowId); ProgramResult = entryPointInterpreter.Invoke(); if (IsAborted) { return; } if (_uiProgram != null && ProgramResult == null) { Exception eventException = null; ThreadHelper.RunOnStaThread(() => { if (BaZicInterpreter.Verbose) { VerboseLog(L.BaZic.Runtime.Interpreters.ProgramInterpreter.LoadingUi); } _uiThread = Thread.CurrentThread; LoadUserInterface(); if (UserInterface == null) { BaZicInterpreter.ChangeState(this, new UiException(L.BaZic.Parser.XamlUnknownParsingError)); return; } UIDispatcher = UserInterface.Dispatcher; if (BaZicInterpreter.Verbose) { VerboseLog(L.BaZic.Runtime.Interpreters.ProgramInterpreter.DeclaringEvents); } foreach (var uiEvent in _uiProgram.UiEvents) { var targetObject = UserInterface.FindName(uiEvent.ControlName); if (targetObject == null) { BaZicInterpreter.ChangeState(this, new UiException($"Unable to find the control named '{uiEvent.ControlName}'.")); return; } var action = new Action(() => { if (BaZicInterpreter.Verbose) { VerboseLog(L.BaZic.Runtime.Interpreters.ProgramInterpreter.EventRaised); } BaZicInterpreter.ChangeState(this, new BaZicInterpreterStateChangeEventArgs(BaZicInterpreterState.Running)); var eventMethodDeclaration = _uiProgram.Methods.Single(m => m.Id == uiEvent.MethodId); var eventInvocation = new InvokeMethodExpression(eventMethodDeclaration.Name.Identifier, false); var eventMethodInterpreter = new MethodInterpreter(BaZicInterpreter, this, eventMethodDeclaration, eventInvocation, ExecutionFlowId); if (targetObject is Window && uiEvent.ControlEventName == nameof(Window.Closed)) { ProgramResult = eventMethodInterpreter.Invoke(); } else { eventMethodInterpreter.Invoke(); } BaZicInterpreter.RunningStateManager.SetIsRunningMainFunction(false); // In all cases, at this point, the main function is done. The UI is running. Idle state must be able to be setted. BaZicInterpreter.RunningStateManager.UpdateState(); }); BaZicInterpreter.Reflection.SubscribeEvent(targetObject, uiEvent.ControlEventName, action); } if (BaZicInterpreter.Verbose) { VerboseLog(L.BaZic.Runtime.Interpreters.ProgramInterpreter.DeclaringBindings); } foreach (var controlAccessor in _uiProgram.UiControlAccessors) { AddVariable(controlAccessor); } if (BaZicInterpreter.Verbose) { VerboseLog(L.BaZic.Runtime.Interpreters.ProgramInterpreter.ShowUi); } var window = UserInterface as Window; try { if (window != null) { window.Closed += (sender, e) => { if (BaZicInterpreter.Verbose) { VerboseLog(L.BaZic.Runtime.Interpreters.ProgramInterpreter.CloseUi); } UserInterface?.Dispatcher?.InvokeShutdown(); }; } BaZicInterpreter.ChangeState(this, new BaZicInterpreterStateChangeEventArgs(BaZicInterpreterState.Idle)); window?.Show(); Dispatcher.Run(); } catch (Exception exception) { CoreHelper.ReportException(exception); eventException = exception; } finally { try { window?.Close(); } catch (Exception exception) { CoreHelper.ReportException(exception); } finally { RuntimeResourceManager.DeleteResources(ExecutionFlowId.ToString()); foreach (var variable in Variables) { variable.Dispose(); } BaZicInterpreter.Reflection.UnsubscribeAllEvents(); UserInterface = null; } } }); if (eventException != null) { throw eventException; } } }
/// <summary> /// Initializes a new instance of the <see cref="Call"/> class. /// </summary> /// <param name="invokeMethodExpression">The reference to the called method.</param> public Call(InvokeMethodExpression invokeMethodExpression) { InvokeMethodExpression = invokeMethodExpression; Variables = new List <Variable>(); }