Implement defining functions
This commit is contained in:
parent
58b5a7355e
commit
07660b6c46
|
@ -0,0 +1,19 @@
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Upsilon.Binder;
|
||||||
|
|
||||||
|
namespace Upsilon.BaseTypes
|
||||||
|
{
|
||||||
|
public class LuaFunction : LuaType
|
||||||
|
{
|
||||||
|
public LuaFunction(ImmutableArray<VariableSymbol> parameters, BoundBlockStatement block)
|
||||||
|
{
|
||||||
|
Parameters = parameters;
|
||||||
|
Block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Type Type => Type.Function;
|
||||||
|
|
||||||
|
public ImmutableArray<VariableSymbol> Parameters { get; }
|
||||||
|
public BoundBlockStatement Block { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ namespace Upsilon.BaseTypes
|
||||||
{
|
{
|
||||||
public enum Type
|
public enum Type
|
||||||
{
|
{
|
||||||
|
Unknown,
|
||||||
Nil,
|
Nil,
|
||||||
Boolean,
|
Boolean,
|
||||||
Number,
|
Number,
|
||||||
|
|
|
@ -12,10 +12,10 @@ namespace Upsilon.Binder
|
||||||
private readonly Diagnostics _diagnostics;
|
private readonly Diagnostics _diagnostics;
|
||||||
private BoundScope _scope;
|
private BoundScope _scope;
|
||||||
|
|
||||||
public Binder(BoundScope parentScope, Diagnostics diagnostics)
|
public Binder(Diagnostics diagnostics)
|
||||||
{
|
{
|
||||||
_diagnostics = diagnostics;
|
_diagnostics = diagnostics;
|
||||||
_scope = new BoundScope(parentScope);
|
_scope = new BoundScope(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundScript BindScript(BlockStatementSyntax e)
|
public BoundScript BindScript(BlockStatementSyntax e)
|
||||||
|
@ -24,7 +24,7 @@ namespace Upsilon.Binder
|
||||||
return new BoundScript(bound);
|
return new BoundScript(bound);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundStatement BindStatement(StatementSyntax s)
|
private BoundStatement BindStatement(StatementSyntax s)
|
||||||
{
|
{
|
||||||
switch (s.Kind)
|
switch (s.Kind)
|
||||||
{
|
{
|
||||||
|
@ -36,12 +36,14 @@ namespace Upsilon.Binder
|
||||||
return BindBlockStatement((BlockStatementSyntax) s);
|
return BindBlockStatement((BlockStatementSyntax) s);
|
||||||
case SyntaxKind.IfStatement:
|
case SyntaxKind.IfStatement:
|
||||||
return BindIfStatement((IfStatementSyntax) s);
|
return BindIfStatement((IfStatementSyntax) s);
|
||||||
|
case SyntaxKind.FunctionStatement:
|
||||||
|
return BindFunctionStatement((FunctionStatementSyntax) s);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotImplementedException(s.Kind.ToString());
|
throw new NotImplementedException(s.Kind.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundExpression BindExpression(ExpressionSyntax e)
|
private BoundExpression BindExpression(ExpressionSyntax e)
|
||||||
{
|
{
|
||||||
switch (e.Kind)
|
switch (e.Kind)
|
||||||
{
|
{
|
||||||
|
@ -177,19 +179,21 @@ namespace Upsilon.Binder
|
||||||
private BoundStatement BindBlockStatement(BlockStatementSyntax e)
|
private BoundStatement BindBlockStatement(BlockStatementSyntax e)
|
||||||
{
|
{
|
||||||
var arr = ImmutableArray.CreateBuilder<BoundStatement>();
|
var arr = ImmutableArray.CreateBuilder<BoundStatement>();
|
||||||
var innerBinder = new Binder(_scope, _diagnostics);
|
|
||||||
foreach (var statementSyntax in e.Statements)
|
foreach (var statementSyntax in e.Statements)
|
||||||
{
|
{
|
||||||
var bound = innerBinder.BindStatement(statementSyntax);
|
var bound = BindStatement(statementSyntax);
|
||||||
arr.Add(bound);
|
arr.Add(bound);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BoundBlockStatement(arr.ToImmutable());
|
return new BoundBlockStatement(arr.ToImmutable());
|
||||||
}
|
}
|
||||||
|
|
||||||
private BoundStatement BindIfStatement(IfStatementSyntax e)
|
private BoundStatement BindIfStatement(IfStatementSyntax e)
|
||||||
{
|
{
|
||||||
|
_scope = new BoundScope(_scope);
|
||||||
var condition = BindExpressionStatement(e.Condition);
|
var condition = BindExpressionStatement(e.Condition);
|
||||||
var block = BindBlockStatement(e.Block);
|
var block = BindBlockStatement(e.Block);
|
||||||
|
_scope = _scope.ParentScope;
|
||||||
if (e.NextElseIfStatement != null)
|
if (e.NextElseIfStatement != null)
|
||||||
{
|
{
|
||||||
var nextElseIf = BindIfStatement(e.NextElseIfStatement);
|
var nextElseIf = BindIfStatement(e.NextElseIfStatement);
|
||||||
|
@ -202,12 +206,60 @@ namespace Upsilon.Binder
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_scope = new BoundScope(_scope);
|
||||||
var elseBlock = BindBlockStatement(e.ElseStatement.Block);
|
var elseBlock = BindBlockStatement(e.ElseStatement.Block);
|
||||||
var elseStatement = new BoundElseStatement((BoundBlockStatement) elseBlock);
|
var elseStatement = new BoundElseStatement((BoundBlockStatement) elseBlock);
|
||||||
|
_scope = _scope.ParentScope;
|
||||||
|
|
||||||
return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block,
|
return new BoundIfStatement((BoundExpressionStatement) condition, (BoundBlockStatement) block,
|
||||||
elseStatement);
|
elseStatement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BoundStatement BindFunctionStatement(FunctionStatementSyntax e)
|
||||||
|
{
|
||||||
|
var name = e.Identifier.Name;
|
||||||
|
var isLocal = e.LocalToken != null;
|
||||||
|
|
||||||
|
var innerScope = new BoundScope(_scope);
|
||||||
|
var parameters = ImmutableArray.CreateBuilder<VariableSymbol>();
|
||||||
|
foreach (var identifierToken in e.Parameters)
|
||||||
|
{
|
||||||
|
var vari = new VariableSymbol(identifierToken.Name, Type.Unknown, true);
|
||||||
|
parameters.Add(vari);
|
||||||
|
innerScope.SetVariable(vari);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_scope.TryGetVariable(name, !isLocal, out var variable))
|
||||||
|
{
|
||||||
|
variable = new FunctionVariableSymbol(name, Type.Function, isLocal, parameters.ToImmutable());
|
||||||
|
if (isLocal)
|
||||||
|
_scope.SetVariable(variable);
|
||||||
|
else
|
||||||
|
_scope.SetGlobalVariable(variable);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables
|
||||||
|
if (variable.Type != Type.Function)
|
||||||
|
{
|
||||||
|
if (variable.Type == Type.Nil )
|
||||||
|
{
|
||||||
|
variable.Type = Type.Function;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_diagnostics.LogCannotConvert(Type.Function, variable.Type, e.Span);
|
||||||
|
return new BoundExpressionStatement(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_scope = innerScope;
|
||||||
|
var block = BindBlockStatement(e.Block);
|
||||||
|
_scope = _scope.ParentScope;
|
||||||
|
return new BoundFunctionStatement(variable, parameters.ToImmutable(), (BoundBlockStatement) block);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,6 +14,7 @@ namespace Upsilon.Binder
|
||||||
BoundExpressionStatement,
|
BoundExpressionStatement,
|
||||||
BoundBlockStatement,
|
BoundBlockStatement,
|
||||||
BoundIfStatement,
|
BoundIfStatement,
|
||||||
BoundElseStatement
|
BoundElseStatement,
|
||||||
|
BoundFunctionStatement
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,20 +6,21 @@ namespace Upsilon.Binder
|
||||||
{
|
{
|
||||||
public class BoundScope
|
public class BoundScope
|
||||||
{
|
{
|
||||||
private readonly BoundScope _parentScope;
|
public readonly BoundScope ParentScope;
|
||||||
private readonly Dictionary<string, VariableSymbol> _variables;
|
private readonly Dictionary<string, VariableSymbol> _variables;
|
||||||
|
|
||||||
public BoundScope(BoundScope parentScope)
|
public BoundScope(BoundScope parentScope)
|
||||||
{
|
{
|
||||||
_parentScope = parentScope;
|
ParentScope = parentScope;
|
||||||
_variables = new Dictionary<string, VariableSymbol>();
|
_variables = new Dictionary<string, VariableSymbol>();
|
||||||
}
|
}
|
||||||
public BoundScope(Dictionary<VariableSymbol, LuaType> variables, BoundScope parentScope)
|
public BoundScope(Dictionary<VariableSymbol, LuaType> variables, BoundScope parentScope)
|
||||||
{
|
{
|
||||||
_parentScope = parentScope;
|
ParentScope = parentScope;
|
||||||
_variables = variables.ToDictionary(x => x.Key.Name, x => x.Key);
|
_variables = variables.ToDictionary(x => x.Key.Name, x => x.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void SetVariable(VariableSymbol var)
|
public void SetVariable(VariableSymbol var)
|
||||||
{
|
{
|
||||||
if (_variables.ContainsKey(var.Name))
|
if (_variables.ContainsKey(var.Name))
|
||||||
|
@ -30,13 +31,13 @@ namespace Upsilon.Binder
|
||||||
|
|
||||||
public void SetGlobalVariable(VariableSymbol var)
|
public void SetGlobalVariable(VariableSymbol var)
|
||||||
{
|
{
|
||||||
if (_parentScope == null)
|
if (ParentScope == null)
|
||||||
{
|
{
|
||||||
SetVariable(var);
|
SetVariable(var);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_parentScope.SetGlobalVariable(var);
|
ParentScope.SetGlobalVariable(var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,9 +48,9 @@ namespace Upsilon.Binder
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (_parentScope != null && allowUpperScopes)
|
if (ParentScope != null && allowUpperScopes)
|
||||||
{
|
{
|
||||||
return _parentScope.TryGetVariable(key, true, out result);
|
return ParentScope.TryGetVariable(key, true, out result);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
|
namespace Upsilon.Binder
|
||||||
|
{
|
||||||
|
public class BoundFunctionStatement : BoundStatement
|
||||||
|
{
|
||||||
|
public VariableSymbol Identifier { get; }
|
||||||
|
public ImmutableArray<VariableSymbol> Parameters { get; }
|
||||||
|
public BoundBlockStatement Block { get; }
|
||||||
|
|
||||||
|
public BoundFunctionStatement(VariableSymbol identifier, ImmutableArray<VariableSymbol> parameters,
|
||||||
|
BoundBlockStatement block)
|
||||||
|
{
|
||||||
|
Identifier = identifier;
|
||||||
|
Parameters = parameters;
|
||||||
|
Block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override BoundKind Kind => BoundKind.BoundFunctionStatement;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Collections.Immutable;
|
||||||
using Upsilon.BaseTypes;
|
using Upsilon.BaseTypes;
|
||||||
|
|
||||||
namespace Upsilon.Binder
|
namespace Upsilon.Binder
|
||||||
|
@ -15,4 +16,15 @@ namespace Upsilon.Binder
|
||||||
public bool Local { get; }
|
public bool Local { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class FunctionVariableSymbol : VariableSymbol
|
||||||
|
{
|
||||||
|
public ImmutableArray<VariableSymbol> Parameters { get; }
|
||||||
|
|
||||||
|
public FunctionVariableSymbol(string name, Type type, bool local, ImmutableArray<VariableSymbol> parameters)
|
||||||
|
: base(name, type, local)
|
||||||
|
{
|
||||||
|
Parameters = parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -25,6 +25,11 @@ namespace Upsilon
|
||||||
Log(DiagnosticLevel.Error, message, location);
|
Log(DiagnosticLevel.Error, message, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LogBadCharacter(TextSpan location, SyntaxKind expectedToken, SyntaxKind currentKind)
|
||||||
|
{
|
||||||
|
LogError($"Invalid character found. Expected: '{expectedToken}', Got: '{currentKind}'", location);
|
||||||
|
}
|
||||||
|
|
||||||
public void LogBadCharacter(TextSpan location, SyntaxKind expectedToken)
|
public void LogBadCharacter(TextSpan location, SyntaxKind expectedToken)
|
||||||
{
|
{
|
||||||
LogError($"Invalid character found. Expected: '{expectedToken}'", location);
|
LogError($"Invalid character found. Expected: '{expectedToken}'", location);
|
||||||
|
|
|
@ -36,6 +36,9 @@ namespace Upsilon.Evaluator
|
||||||
case BoundKind.BoundIfStatement:
|
case BoundKind.BoundIfStatement:
|
||||||
EvaluateBoundIfStatement((BoundIfStatement) e);
|
EvaluateBoundIfStatement((BoundIfStatement) e);
|
||||||
break;
|
break;
|
||||||
|
case BoundKind.BoundFunctionStatement:
|
||||||
|
EvaluateBoundFunctionStatement((BoundFunctionStatement) e);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
||||||
break;
|
break;
|
||||||
|
@ -155,5 +158,14 @@ namespace Upsilon.Evaluator
|
||||||
EvaluateBoundBlockStatement(boundBlockStatement.ElseStatement.Block);
|
EvaluateBoundBlockStatement(boundBlockStatement.ElseStatement.Block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EvaluateBoundFunctionStatement(BoundFunctionStatement boundFunctionStatement)
|
||||||
|
{
|
||||||
|
var func = new LuaFunction(boundFunctionStatement.Parameters, boundFunctionStatement.Block);
|
||||||
|
if (boundFunctionStatement.Identifier.Local)
|
||||||
|
_scope.Set(boundFunctionStatement.Identifier, func);
|
||||||
|
else
|
||||||
|
_scope.SetGlobal(boundFunctionStatement.Identifier, func);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ using Upsilon.BaseTypes;
|
||||||
using Upsilon.Binder;
|
using Upsilon.Binder;
|
||||||
using Upsilon.Parser;
|
using Upsilon.Parser;
|
||||||
using Upsilon.Text;
|
using Upsilon.Text;
|
||||||
|
using Upsilon.Utilities;
|
||||||
|
|
||||||
namespace Upsilon.Evaluator
|
namespace Upsilon.Evaluator
|
||||||
{
|
{
|
||||||
|
@ -22,7 +23,7 @@ namespace Upsilon.Evaluator
|
||||||
_parsed = Parser.Parser.Parse(scriptString, Diagnostics);
|
_parsed = Parser.Parser.Parse(scriptString, Diagnostics);
|
||||||
if (variables == null)
|
if (variables == null)
|
||||||
variables = new Dictionary<VariableSymbol, LuaType>();
|
variables = new Dictionary<VariableSymbol, LuaType>();
|
||||||
Binder = new Binder.Binder(new BoundScope(variables, null), Diagnostics);
|
Binder = new Binder.Binder(Diagnostics);
|
||||||
Scope = new EvaluationScope(variables);
|
Scope = new EvaluationScope(variables);
|
||||||
Evaluator = new Evaluator( Diagnostics, Scope);
|
Evaluator = new Evaluator( Diagnostics, Scope);
|
||||||
}
|
}
|
||||||
|
@ -41,5 +42,10 @@ namespace Upsilon.Evaluator
|
||||||
{
|
{
|
||||||
return (T)Evaluator.Evaluate(Bind());
|
return (T)Evaluator.Evaluate(Bind());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string PrettyPrintSyntaxTree()
|
||||||
|
{
|
||||||
|
return _parsed.Print();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -82,6 +82,8 @@ namespace Upsilon.Parser
|
||||||
return new SyntaxToken(SyntaxKind.OpenParenthesis, _position, "(", null);
|
return new SyntaxToken(SyntaxKind.OpenParenthesis, _position, "(", null);
|
||||||
case ')':
|
case ')':
|
||||||
return new SyntaxToken(SyntaxKind.CloseParenthesis, _position, ")", null);
|
return new SyntaxToken(SyntaxKind.CloseParenthesis, _position, ")", null);
|
||||||
|
case ',':
|
||||||
|
return new SyntaxToken(SyntaxKind.Comma, _position, ",", null);
|
||||||
case '=':
|
case '=':
|
||||||
if (Next == '=')
|
if (Next == '=')
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace Upsilon.Parser
|
||||||
if (Current.Kind == kind)
|
if (Current.Kind == kind)
|
||||||
return NextToken();
|
return NextToken();
|
||||||
|
|
||||||
_diagnostics.LogBadCharacter(Current.Span, kind);
|
_diagnostics.LogBadCharacter(Current.Span, kind, Current.Kind);
|
||||||
return new SyntaxToken(kind, Current.Span.Start, "", null);
|
return new SyntaxToken(kind, Current.Span.Start, "", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +71,15 @@ namespace Upsilon.Parser
|
||||||
return ParseIfStatement(SyntaxKind.IfKeyword);
|
return ParseIfStatement(SyntaxKind.IfKeyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Current.Kind == SyntaxKind.FunctionKeyword)
|
||||||
|
{
|
||||||
|
return ParseFunctionStatement();
|
||||||
|
}
|
||||||
|
if (Current.Kind == SyntaxKind.LocalKeyword && Next.Kind == SyntaxKind.FunctionKeyword)
|
||||||
|
{
|
||||||
|
return ParseFunctionStatement();
|
||||||
|
}
|
||||||
|
|
||||||
return ParseExpressionStatement();
|
return ParseExpressionStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +123,31 @@ namespace Upsilon.Parser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private StatementSyntax ParseFunctionStatement()
|
||||||
|
{
|
||||||
|
SyntaxToken localToken = null;
|
||||||
|
if (Current.Kind == SyntaxKind.LocalKeyword)
|
||||||
|
{
|
||||||
|
localToken = NextToken();
|
||||||
|
}
|
||||||
|
var functionToken = MatchToken(SyntaxKind.FunctionKeyword);
|
||||||
|
var identifier = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
|
||||||
|
var openParenthesis = MatchToken(SyntaxKind.OpenParenthesis);
|
||||||
|
var variableBuilder = ImmutableArray.CreateBuilder<IdentifierToken>();
|
||||||
|
while (Current.Kind != SyntaxKind.CloseParenthesis)
|
||||||
|
{
|
||||||
|
var variableIdentifier = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
|
||||||
|
variableBuilder.Add(variableIdentifier);
|
||||||
|
if (Current.Kind == SyntaxKind.Comma)
|
||||||
|
NextToken();
|
||||||
|
}
|
||||||
|
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
||||||
|
var block = ParseBlockStatement(new[] {SyntaxKind.EndKeyword});
|
||||||
|
var endToken = MatchToken(SyntaxKind.EndKeyword);
|
||||||
|
return new FunctionStatementSyntax(localToken, functionToken, identifier, openParenthesis,
|
||||||
|
variableBuilder.ToImmutable(), closeParenthesis, (BlockStatementSyntax) block, endToken);
|
||||||
|
}
|
||||||
|
|
||||||
private ExpressionStatementSyntax ParseExpressionStatement()
|
private ExpressionStatementSyntax ParseExpressionStatement()
|
||||||
{
|
{
|
||||||
var expression = ParseExpression();
|
var expression = ParseExpression();
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
|
namespace Upsilon.Parser
|
||||||
|
{
|
||||||
|
public class FunctionStatementSyntax : StatementSyntax
|
||||||
|
{
|
||||||
|
public SyntaxToken LocalToken { get; }
|
||||||
|
public SyntaxToken FunctionToken { get; }
|
||||||
|
public IdentifierToken Identifier { get; }
|
||||||
|
public SyntaxToken OpenParenthesis { get; }
|
||||||
|
public ImmutableArray<IdentifierToken> Parameters { get; }
|
||||||
|
public SyntaxToken CloseParenthesis { get; }
|
||||||
|
public BlockStatementSyntax Block { get; }
|
||||||
|
public SyntaxToken EndToken { get; }
|
||||||
|
|
||||||
|
public FunctionStatementSyntax(SyntaxToken localToken, SyntaxToken functionToken, IdentifierToken identifier,
|
||||||
|
SyntaxToken openParenthesis, ImmutableArray<IdentifierToken> parameters, SyntaxToken closeParenthesis,
|
||||||
|
BlockStatementSyntax block, SyntaxToken endToken)
|
||||||
|
{
|
||||||
|
LocalToken = localToken;
|
||||||
|
FunctionToken = functionToken;
|
||||||
|
Identifier = identifier;
|
||||||
|
OpenParenthesis = openParenthesis;
|
||||||
|
Parameters = parameters;
|
||||||
|
CloseParenthesis = closeParenthesis;
|
||||||
|
Block = block;
|
||||||
|
EndToken = endToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override SyntaxKind Kind => SyntaxKind.FunctionStatement;
|
||||||
|
public override IEnumerable<SyntaxNode> ChildNodes()
|
||||||
|
{
|
||||||
|
yield return LocalToken;
|
||||||
|
yield return FunctionToken;
|
||||||
|
yield return Identifier;
|
||||||
|
yield return OpenParenthesis;
|
||||||
|
foreach (var identifierToken in Parameters)
|
||||||
|
{
|
||||||
|
yield return identifierToken;
|
||||||
|
}
|
||||||
|
yield return CloseParenthesis;
|
||||||
|
yield return Block;
|
||||||
|
yield return EndToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,8 @@ namespace Upsilon.Parser
|
||||||
return SyntaxKind.ElseKeyword;
|
return SyntaxKind.ElseKeyword;
|
||||||
case "nil":
|
case "nil":
|
||||||
return SyntaxKind.NilKeyword;
|
return SyntaxKind.NilKeyword;
|
||||||
|
case "function":
|
||||||
|
return SyntaxKind.FunctionKeyword;
|
||||||
default:
|
default:
|
||||||
return SyntaxKind.Identifier;
|
return SyntaxKind.Identifier;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ namespace Upsilon.Parser
|
||||||
EqualsEquals,
|
EqualsEquals,
|
||||||
Tilde,
|
Tilde,
|
||||||
TildeEquals,
|
TildeEquals,
|
||||||
|
Comma,
|
||||||
|
|
||||||
// key words
|
// key words
|
||||||
TrueKeyword,
|
TrueKeyword,
|
||||||
|
@ -33,6 +34,7 @@ namespace Upsilon.Parser
|
||||||
ElseIfKeyword,
|
ElseIfKeyword,
|
||||||
ElseKeyword,
|
ElseKeyword,
|
||||||
NilKeyword,
|
NilKeyword,
|
||||||
|
FunctionKeyword,
|
||||||
|
|
||||||
Identifier,
|
Identifier,
|
||||||
|
|
||||||
|
@ -54,5 +56,6 @@ namespace Upsilon.Parser
|
||||||
IfStatement,
|
IfStatement,
|
||||||
ElseIfStatement,
|
ElseIfStatement,
|
||||||
ElseStatement,
|
ElseStatement,
|
||||||
|
FunctionStatement,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using Upsilon.BaseTypes;
|
||||||
|
using Upsilon.BaseTypes.Number;
|
||||||
|
using Upsilon.Evaluator;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace UpsilonTests
|
||||||
|
{
|
||||||
|
public class FunctionTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void BasicFunctionTest()
|
||||||
|
{
|
||||||
|
const string input = @"
|
||||||
|
function testFunc ()
|
||||||
|
a = 100
|
||||||
|
end
|
||||||
|
a = 50
|
||||||
|
testFunc()
|
||||||
|
";
|
||||||
|
var script = new Script(input);
|
||||||
|
Assert.Empty(script.Diagnostics.Messages);
|
||||||
|
script.Evaluate();
|
||||||
|
Assert.Empty(script.Diagnostics.Messages);
|
||||||
|
Assert.True(script.Scope.TryGet("testFunc", out var func));
|
||||||
|
Assert.IsType<LuaFunction>(func);
|
||||||
|
Assert.True(script.Scope.TryGet("a", out var a));
|
||||||
|
Assert.Equal(100, (long)(NumberLong)a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,8 @@ namespace Ycicle
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsed = new Script(input, variables);
|
var parsed = new Script(input, variables);
|
||||||
|
Console.WriteLine(parsed.PrettyPrintSyntaxTree());
|
||||||
|
|
||||||
if (parsed.Diagnostics.Messages.Count > 0)
|
if (parsed.Diagnostics.Messages.Count > 0)
|
||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
|
Loading…
Reference in New Issue