diff --git a/Upsilon/BaseTypes/TypeConversion.cs b/Upsilon/BaseTypes/TypeConversion.cs index e2cfc0d..715658e 100644 --- a/Upsilon/BaseTypes/TypeConversion.cs +++ b/Upsilon/BaseTypes/TypeConversion.cs @@ -42,5 +42,33 @@ namespace Upsilon.BaseTypes } return new GenericUserData(o); } + + public static Type GetLuaType(object o) + { + if (o is ScriptType t) + { + return t.Type; + } + + switch (o) + { + case bool _: + return Type.Boolean; + case int _: + return Type.Number; + case long _: + return Type.Number; + case float _: + return Type.Number; + case double _: + return Type.Number; + case string _: + return Type.String; + case null: + return Type.Nil; + default: + return Type.UserData; + } + } } } \ No newline at end of file diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index c5772d0..836cee2 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -50,7 +50,11 @@ namespace Upsilon.Binder } unboundFunctionStatement.Value.Block = (BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.Value.UnboundBlock); + var resultType = Scope.ReturnType; Scope = Scope.ParentScope; + var variable = (FunctionVariableSymbol)Scope.Variables[unboundFunctionStatement.Key]; + variable.IsBound = true; + variable.ResultType = resultType; } _unboundFunctions = new Dictionary(); return new BoundScript((BoundBlockStatement) bound, e.Span, Scope); @@ -198,6 +202,7 @@ namespace Upsilon.Binder parameters.Add(bound); } + var returnType = Type.Unknown; if (expression.Kind == BoundKind.VariableExpression) { var variableExpression =(BoundVariableExpression) expression; @@ -216,14 +221,18 @@ namespace Upsilon.Binder var unboundFunctionStatement = _unboundFunctions[function.Name]; unboundFunctionStatement.Block = (BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock); + returnType = Scope.ReturnType; Scope = Scope.ParentScope; function.IsBound = true; + function.ResultType = returnType; _unboundFunctions.Remove(function.Name); } + + returnType = function.ResultType; } //TODO: validate parameters - return new BoundFunctionCallExpression(expression, parameters.ToImmutable(), e.Span); + return new BoundFunctionCallExpression(expression, parameters.ToImmutable(), e.Span, returnType); } private BoundExpression BindVariableExpression(VariableExpressionSyntax e) @@ -402,9 +411,10 @@ namespace Upsilon.Binder { Scope = innerScope; var block = BindBlockStatement(e.Block); + var returnType = Scope.ReturnType; Scope = Scope.ParentScope; var func = new BoundFunctionExpression(parameters.ToImmutable(), (BoundBlockStatement) block, e.Span, - innerScope); + innerScope, returnType); return func; } else @@ -445,7 +455,7 @@ namespace Upsilon.Binder if (!Scope.TryGetVariable(name, !isLocal, out var variable)) { - var functionVariable = new FunctionVariableSymbol(name, Type.Function, isLocal, parameters.ToImmutable()) + var functionVariable = new FunctionVariableSymbol(name, isLocal, parameters.ToImmutable(), func.ReturnType) { CommentValue = commentData.ToArray() }; @@ -480,6 +490,17 @@ namespace Upsilon.Binder private BoundStatement BindReturnStatement(ReturnStatementSyntax e) { var expression = BindExpression(e.Expression); + if (expression.Type != Type.Unknown && expression.Type != Type.Unknown && + Scope.ReturnType != Type.Unknown && Scope.ReturnType != Type.Nil) + { + if (expression.Type != Scope.ReturnType) + { + _diagnostics.LogError($"Can't return type '{expression.Type}' from this scope, earlier in the" + + $" scope a return type of '{Scope.ReturnType}' is defined.", e.Span); + } + } + if (expression.Type != Type.Unknown && expression.Type != Type.Nil) + Scope.ReturnType = expression.Type; return new BoundReturnStatement(expression, e.Span); } diff --git a/Upsilon/Binder/BoundExpressions/BoundFunctionCallExpression.cs b/Upsilon/Binder/BoundExpressions/BoundFunctionCallExpression.cs index 6302772..e16a418 100644 --- a/Upsilon/Binder/BoundExpressions/BoundFunctionCallExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundFunctionCallExpression.cs @@ -11,10 +11,11 @@ namespace Upsilon.Binder public ImmutableArray Parameters { get; } public BoundFunctionCallExpression(BoundExpression identifier, ImmutableArray parameters, - TextSpan span) : base(span) + TextSpan span, Type type) : base(span) { Identifier = identifier; Parameters = parameters; + Type = type; } public override BoundKind Kind => BoundKind.BoundFunctionCallExpression; @@ -37,6 +38,6 @@ namespace Upsilon.Binder yield return this; } - public override Type Type => Type.Unknown; + public override Type Type { get; } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundExpressions/BoundFunctionExpression.cs b/Upsilon/Binder/BoundExpressions/BoundFunctionExpression.cs index 49819ec..488e3a7 100644 --- a/Upsilon/Binder/BoundExpressions/BoundFunctionExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundFunctionExpression.cs @@ -10,12 +10,13 @@ namespace Upsilon.Binder public ImmutableArray Parameters { get; } public BoundBlockStatement Block { get; set; } - public BoundFunctionExpression(ImmutableArray parameters, BoundBlockStatement block, TextSpan span, - BoundScope scope) : base(span) + public BoundFunctionExpression(ImmutableArray parameters, BoundBlockStatement block, + TextSpan span, BoundScope scope, Type returnType) : base(span) { Parameters = parameters; Block = block; Scope = scope; + ReturnType = returnType; } public override BoundKind Kind => BoundKind.BoundFunctionExpression; @@ -35,5 +36,6 @@ namespace Upsilon.Binder public override Type Type => Type.Function; public BoundScope Scope { get; set; } + public Type ReturnType { get; } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundScope.cs b/Upsilon/Binder/BoundScope.cs index dce4b43..25eeefc 100644 --- a/Upsilon/Binder/BoundScope.cs +++ b/Upsilon/Binder/BoundScope.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Type = Upsilon.BaseTypes.Type; namespace Upsilon.Binder { @@ -8,6 +9,7 @@ namespace Upsilon.Binder public readonly BoundScope ParentScope; public BoundScope ReadOnlyScope { get; private set; } public readonly Dictionary Variables; + public Type ReturnType { get; set; } = Type.Nil; public BoundScope(BoundScope parentScope) { diff --git a/Upsilon/Binder/BoundStatements/UnboundFunctionExpression.cs b/Upsilon/Binder/BoundStatements/UnboundFunctionExpression.cs index e563951..f2f3f95 100644 --- a/Upsilon/Binder/BoundStatements/UnboundFunctionExpression.cs +++ b/Upsilon/Binder/BoundStatements/UnboundFunctionExpression.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Collections.Immutable; +using Upsilon.BaseTypes; using Upsilon.Parser; using Upsilon.Text; @@ -8,7 +9,8 @@ namespace Upsilon.Binder public class UnboundFunctionExpression : BoundFunctionExpression { public UnboundFunctionExpression(ImmutableArray parameters, - BlockStatementSyntax unboundBlock, TextSpan span, BoundScope scope) : base(parameters, null, span, scope) + BlockStatementSyntax unboundBlock, TextSpan span, BoundScope scope) + : base(parameters, null, span, scope, Type.Unknown) { UnboundBlock = unboundBlock; } diff --git a/Upsilon/Binder/VariableSymbol.cs b/Upsilon/Binder/VariableSymbol.cs index eaac21e..276813b 100644 --- a/Upsilon/Binder/VariableSymbol.cs +++ b/Upsilon/Binder/VariableSymbol.cs @@ -23,12 +23,14 @@ namespace Upsilon.Binder public class FunctionVariableSymbol : VariableSymbol { public ImmutableArray Parameters { get; } + public Type ResultType { get; internal set; } public bool IsBound { get; set; } - public FunctionVariableSymbol(string name, Type type, bool local, ImmutableArray parameters) - : base(name, type, local) + public FunctionVariableSymbol(string name, bool local, ImmutableArray parameters, Type resultType) + : base(name, Type.Function, local) { Parameters = parameters; + ResultType = resultType; } } diff --git a/Upsilon/StandardLibraries/StaticScope.cs b/Upsilon/StandardLibraries/StaticScope.cs index a5f2190..3308edd 100644 --- a/Upsilon/StandardLibraries/StaticScope.cs +++ b/Upsilon/StandardLibraries/StaticScope.cs @@ -46,8 +46,8 @@ namespace Upsilon.StandardLibraries var boundScope = new BoundScope( scope.Variables.ToDictionary(x => x.Key, - x => (VariableSymbol) new FunctionVariableSymbol(x.Key, x.Value.Type, false, - ImmutableArray.Empty) {IsBound = true}), + x => (VariableSymbol) new FunctionVariableSymbol(x.Key, false, + ImmutableArray.Empty, x.Value.Type) {IsBound = true}), null); return (scope, boundScope); }