Skip to content

Commit

Permalink
Intermediate commit because need to checkout other branch
Browse files Browse the repository at this point in the history
  • Loading branch information
Antonia Heinen committed Jan 14, 2021
1 parent 2cb6901 commit def3b28
Show file tree
Hide file tree
Showing 17 changed files with 156 additions and 43 deletions.
5 changes: 3 additions & 2 deletions Source/Analysis/AbstractSyntaxTree/IBindable.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using Krypton.Analysis.AbstractSyntaxTree.Nodes.Identifiers;
using Krypton.Analysis.AbstractSyntaxTree.Nodes;
using Krypton.Analysis.AbstractSyntaxTree.Nodes.Identifiers;
using Krypton.Analysis.AbstractSyntaxTree.Nodes.Symbols;

namespace Krypton.Analysis.AbstractSyntaxTree
{
public interface IBindable
public interface IBindable : INode
{
IdentifierNode IdentifierNode { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,22 @@

namespace Krypton.Analysis.AbstractSyntaxTree.Nodes.Expressions
{
public sealed class BinaryOperationChainNode : ExpressionNode
/* A BinaryOperationChainExpressionNode is a helper node
* that should never escape grammatical analysis.
* It represents multiple binary operations chained one
* after the other (e.g. 4 + 5 * 6).
* Its Resolve method performs the act of turning this
* into the tree 4 + (5 * 6).
* It saves an ordered list of ExpressionNodes that represents
* the operands (0 op 1 op 2 op 3 ...) and one that represents
* the operators (ex 0 ex 1 ex 2 ex ...). In a valid state
* there is exactly one more operand than operator.
* An operation chain can only resolved if it is in such
* a valid state.
*/
public sealed class BinaryOperationChainExpressionNode : ExpressionNode
{
public BinaryOperationChainNode(int lineNumber) : base(lineNumber) { }
public BinaryOperationChainExpressionNode(int lineNumber) : base(lineNumber) { }

private readonly List<IOperatorLexeme> operators = new();
private readonly List<ExpressionNode> operands = new();
Expand All @@ -27,9 +40,21 @@ public void AddOperand(ExpressionNode operand)
operands.Add(operand);
}

public override BinaryOperationChainNode Clone()
public override BinaryOperationChainExpressionNode Clone()
{
throw new NotSupportedException();
Debug.Assert(operands.Count == operators.Count + 1);

BinaryOperationChainExpressionNode newChain = new(LineNumber);

for (int i = 0; i < operators.Count; i++)
{
newChain.AddOperand(operands[i]);
newChain.AddOperator(operators[2]);
}

newChain.AddOperand(operands[^1]);

return newChain;
}

public override void PopulateBranches(List<Node> list)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

namespace Krypton.Analysis.AbstractSyntaxTree.Nodes.Expressions
{
/* A BinaryOperationExpressionNode is any expression
* with a concrete operator lexeme and a sub expression
* on the left hand side and one on the right hand
* side.
*/
public abstract class BinaryOperationExpressionNode : ExpressionNode
{
protected BinaryOperationExpressionNode(ExpressionNode left, ExpressionNode right, int lineNumber) : base(lineNumber)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
namespace Krypton.Analysis.AbstractSyntaxTree.Nodes.Expressions
{
/* An ExpressionNode represents an expression according
* to the spec, so for example:
* - an argument
* - a variable initializer
* - a return value
* - a sub expression
* Branches: none as defined by this abstract class
* LineNumber: the line number of the first lexeme
* that makes up the expression
*/
public abstract class ExpressionNode : Node
{
protected ExpressionNode(int lineNumber) : base(lineNumber) { }
Expand Down
17 changes: 17 additions & 0 deletions Source/Analysis/AbstractSyntaxTree/Nodes/INode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Collections.Generic;

namespace Krypton.Analysis.AbstractSyntaxTree.Nodes
{
public interface INode
{
int LineNumber { get; }

Node? Parent { get; }

Node Clone();

List<Node> GetBranches();

void PopulateBranches(List<Node> list);
}
}
32 changes: 31 additions & 1 deletion Source/Analysis/AbstractSyntaxTree/Nodes/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,38 @@

namespace Krypton.Analysis.AbstractSyntaxTree.Nodes
{
/* A node is a part of the abstract syntax tree.
* Each node also saves the exact line in which
* it occurred in the original code. This is to
* make error messages better. Most nodes logically
* consist of multiple lexemes, so the line number
* will be the line number of the first lexemes
* that makes up the node (for example, the line
* number of an IfStatementNode is the line number
* of the "If" keyword). The first line is 1. If
* the line number is 0, then the node is synthesised
* by the compiler or it representes a builtin type,
* function or constant.
* As the node is part of a tree, it is recursive in
* that every node may save references to other nodes.
* These are called branches of the node.
* The parent of a node is the single node that owns
* a reference to this node in the tree. Some nodes
* like BoundIdentifierNode point across the tree
* to another node. These nodes are not considered
* parent of the other node. The textual equivalence
* of the parent is seen close to the textual equivalence
* of the node in the original code. This does not
* apply to identifier that point across the code.
* A node is able to clone itself. This means that
* a new instance of the node type is created. Every
* branch of the node is cloned as well and put in
* place of the old reference as the branch of the
* cloned node. This rule also applies to the cloning
* of the branches.
*/
[DebuggerDisplay("{GetType().Name} -- {GetHashCode()}")]
public abstract class Node
public abstract class Node : INode
{
protected Node(int lineNumber)
{
Expand Down
3 changes: 3 additions & 0 deletions Source/Analysis/AbstractSyntaxTree/Nodes/ParameterNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace Krypton.Analysis.AbstractSyntaxTree.Nodes
{
/* A ParameterNode represents the declaration of
* a parameter of a function. It is used by
*/
public sealed class ParameterNode : Node
{
public ParameterNode(string identifier, TypeSymbolNode type, int lineNumber) : base(lineNumber)
Expand Down
17 changes: 13 additions & 4 deletions Source/Analysis/AbstractSyntaxTree/Nodes/ScriptNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,33 @@

namespace Krypton.Analysis.AbstractSyntaxTree.Nodes
{
/* A ScriptNode is the root of the syntax tree.
* It represents the whole script. Therefore, it
* saves the top level statements of the script
* and all declared symbols (none yet, but it will
* save functions etc.)
* Branches:
* - A StatementCollection that represents the
* top level statements
*/
public sealed class ScriptNode : Node
{
public ScriptNode(StatementCollectionNode statements, int lineNumber) : base(lineNumber)
{
Statements = statements;
TopLevelStatements = statements;
}

public StatementCollectionNode Statements { get; }
public StatementCollectionNode TopLevelStatements { get; }

public override ScriptNode Clone()
{
return new(Statements.Clone(), LineNumber);
return new(TopLevelStatements.Clone(), LineNumber);
}

public override void PopulateBranches(List<Node> list)
{
list.Add(this);
Statements.PopulateBranches(list);
TopLevelStatements.PopulateBranches(list);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@

namespace Krypton.Analysis.AbstractSyntaxTree.Nodes
{
/* A StatementCollectionNode is a collection of statements.
* It is used by the ScriptNode to represents top level
* statements, by control statements like Block or While
* to represents its nested statements, etc.
* Branches:
* - An ordered list of StatementNodes.
* LineNumber:
* - The line number of the first statement
* - If there are no statements: 0
*/
public sealed class StatementCollectionNode : Node, IIndexedEnumerable<StatementNode>, IEnumerable<StatementNode>
{
public StatementCollectionNode(IEnumerable<StatementNode> statements) : base(statements.FirstOrDefault()?.LineNumber ?? 0)
Expand Down
4 changes: 2 additions & 2 deletions Source/Analysis/Grammatical/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public ExpressionParser(LexemeCollection lexemes)

ParseAfterSubExpression(ref root, ref index);

if (root is BinaryOperationChainNode chain)
if (root is BinaryOperationChainExpressionNode chain)
{
root = chain.Resolve();
}
Expand Down Expand Up @@ -126,7 +126,7 @@ private void ParseOperationChain(IOperatorLexeme opLexeme, ref ExpressionNode? r
return;
}

if (root is not BinaryOperationChainNode chain)
if (root is not BinaryOperationChainExpressionNode chain)
{
chain = new(opLexeme.LineNumber);
chain.AddOperand(root);
Expand Down
9 changes: 9 additions & 0 deletions Source/Analysis/Lexical/Lexemes/ILexeme.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Krypton.Analysis.Lexical.Lexemes
{
public interface ILexeme
{
string Content { get; }

int LineNumber { get; }
}
}
8 changes: 1 addition & 7 deletions Source/Analysis/Lexical/Lexemes/IOperatorLexeme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@ namespace Krypton.Analysis.Lexical.Lexemes
// This interface is intended only to be implemented by classes that inherited from
// Krypton.Analysis.Lexical.Lexemes.Lexeme. It can't be a class, because both
// KeywordLexemes and SyntaxCharacterLexemes can be binary operators.
public interface IOperatorLexeme
public interface IOperatorLexeme : ILexeme
{
// All classes that inherit from Lexeme already own this member.
string Content { get; }

// Same here.
int LineNumber { get; }

OperatorPrecedenceGroup PrecedenceGroup { get; }
}
}
2 changes: 1 addition & 1 deletion Source/Analysis/Lexical/Lexemes/Lexeme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Krypton.Analysis.Lexical.Lexemes
{
[DebuggerDisplay("{DebuggerDisplay()}")]
public abstract class Lexeme
public abstract class Lexeme : ILexeme
{
protected Lexeme(int lineNumber)
{
Expand Down
2 changes: 1 addition & 1 deletion Source/Analysis/Semantical/Binding/TypeAgnosticBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public bool PerformBinding()

// Bind top level statements
LocalVariableIdentifierMap localVariableIdentifierMap = new();
if (!BindLocalVariables(syntaxTree.Root.Statements, localVariableIdentifierMap, globalIdentifierMap))
if (!BindLocalVariables(syntaxTree.Root.TopLevelStatements, localVariableIdentifierMap, globalIdentifierMap))
{
return false;
}
Expand Down
28 changes: 14 additions & 14 deletions UnitTests/BindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ public void SimpleBindingTest()
SyntaxTree? tree = Analyser.Analyse(Code);

Assert.NotNull(tree);
Assert.IsInstanceOf<VariableDeclarationStatementNode>(tree!.Root.Statements[0]);
Assert.IsInstanceOf<VariableAssignmentStatementNode>(tree.Root.Statements[1]);
Assert.IsInstanceOf<VariableDeclarationStatementNode>(tree!.Root.TopLevelStatements[0]);
Assert.IsInstanceOf<VariableAssignmentStatementNode>(tree.Root.TopLevelStatements[1]);

var decl = (VariableDeclarationStatementNode)tree!.Root.Statements[0];
var assg = (VariableAssignmentStatementNode)tree!.Root.Statements[1];
var decl = (VariableDeclarationStatementNode)tree!.Root.TopLevelStatements[0];
var assg = (VariableAssignmentStatementNode)tree!.Root.TopLevelStatements[1];

Assert.IsInstanceOf<BoundIdentifierNode>(decl.VariableIdentifierNode);
Assert.IsInstanceOf<BoundIdentifierNode>(assg.VariableIdentifierNode);
Expand Down Expand Up @@ -66,8 +66,8 @@ public void MultibleVariablesBindingTest()

Assert.NotNull(tree);

var decl1 = tree!.Root.Statements[0] as VariableDeclarationStatementNode;
var decl2 = tree!.Root.Statements[1] as VariableDeclarationStatementNode;
var decl1 = tree!.Root.TopLevelStatements[0] as VariableDeclarationStatementNode;
var decl2 = tree!.Root.TopLevelStatements[1] as VariableDeclarationStatementNode;

Assert.NotNull(decl1);
Assert.NotNull(decl2);
Expand Down Expand Up @@ -114,9 +114,9 @@ public void BuiltinFunctionBindingTest()
SyntaxTree? tree = Analyser.Analyse(Code);

Assert.NotNull(tree);
Assert.IsInstanceOf<FunctionCallStatementNode>(tree!.Root.Statements[0]);
Assert.IsInstanceOf<FunctionCallStatementNode>(tree!.Root.TopLevelStatements[0]);

var call = (FunctionCallStatementNode)tree.Root.Statements[0];
var call = (FunctionCallStatementNode)tree.Root.TopLevelStatements[0];

Assert.IsInstanceOf<IdentifierExpressionNode>(call.FunctionExpression);

Expand Down Expand Up @@ -144,11 +144,11 @@ public void TypeBindingTest()
SyntaxTree? tree = Analyser.Analyse(Code);

Assert.NotNull(tree);
Assert.AreEqual(2, tree!.Root.Statements.Count);
Assert.IsInstanceOf<VariableDeclarationStatementNode>(tree.Root.Statements[0]);
Assert.IsInstanceOf<VariableAssignmentStatementNode>(tree.Root.Statements[1]);
Assert.AreEqual(2, tree!.Root.TopLevelStatements.Count);
Assert.IsInstanceOf<VariableDeclarationStatementNode>(tree.Root.TopLevelStatements[0]);
Assert.IsInstanceOf<VariableAssignmentStatementNode>(tree.Root.TopLevelStatements[1]);

var decl = (VariableDeclarationStatementNode)tree.Root.Statements[0];
var decl = (VariableDeclarationStatementNode)tree.Root.TopLevelStatements[0];

Assert.NotNull(decl.Type);
Assert.IsInstanceOf<IdentifierTypeNode>(decl.Type);
Expand Down Expand Up @@ -179,7 +179,7 @@ public void MoreTypeBindingTest()
SyntaxTree? tree = Analyser.Analyse(Code);

Assert.NotNull(tree);
Assert.AreEqual(4, tree!.Root.Statements.Count);
Assert.AreEqual(4, tree!.Root.TopLevelStatements.Count);

BuiltinIdentifierMap bim = new();
SymbolNode?[] symbols =
Expand All @@ -192,7 +192,7 @@ public void MoreTypeBindingTest()

int i = 0;

foreach (var statement in tree.Root.Statements)
foreach (var statement in tree.Root.TopLevelStatements)
{
Assert.IsInstanceOf<VariableDeclarationStatementNode>(statement);

Expand Down
2 changes: 1 addition & 1 deletion UnitTests/ParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public void SeveralNestedParensTest()
[Test]
public void ChainResolutionTest()
{
BinaryOperationChainNode chain = new(1); // Arbitrary line number
BinaryOperationChainExpressionNode chain = new(1); // Arbitrary line number
chain.AddOperand(new IntegerLiteralExpressionNode(4, 1));
chain.AddOperator(new CharacterOperatorLexeme(CharacterOperator.Plus, 1));
chain.AddOperand(new IntegerLiteralExpressionNode(4, 1));
Expand Down
12 changes: 6 additions & 6 deletions UnitTests/ScriptTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ While True ... 3

ScriptNode scriptNode = tree.Root;

Assert.AreEqual(4, scriptNode.Statements.Count);
Assert.AreEqual(4, scriptNode.TopLevelStatements.Count);

Assert.IsInstanceOf<FunctionCallStatementNode>(scriptNode.Statements[0]);
Assert.IsInstanceOf<VariableDeclarationStatementNode>(scriptNode.Statements[1]);
Assert.IsInstanceOf<FunctionCallStatementNode>(scriptNode.Statements[2]);
Assert.IsInstanceOf<WhileStatementNode>(scriptNode.Statements[3]);
Assert.IsInstanceOf<FunctionCallStatementNode>(scriptNode.TopLevelStatements[0]);
Assert.IsInstanceOf<VariableDeclarationStatementNode>(scriptNode.TopLevelStatements[1]);
Assert.IsInstanceOf<FunctionCallStatementNode>(scriptNode.TopLevelStatements[2]);
Assert.IsInstanceOf<WhileStatementNode>(scriptNode.TopLevelStatements[3]);

WhileStatementNode @while = (WhileStatementNode)scriptNode.Statements[3];
WhileStatementNode @while = (WhileStatementNode)scriptNode.TopLevelStatements[3];

Assert.AreEqual(1, @while.Statements.Count);
}
Expand Down

0 comments on commit def3b28

Please sign in to comment.