From abbd4a74ef61e37c5bdbe4a92041695b20a3bff0 Mon Sep 17 00:00:00 2001 From: Sen Date: Thu, 2 Feb 2023 14:05:53 +0000 Subject: [PATCH] Parser stuff --- ASTNodeAdd.cs | 20 +++ ASTNodeBinaryNumericOperator.cs | 53 +++++++ ASTNodeBlock.cs | 96 +++++++++++++ ASTNodeComparison.cs | 134 ++++++++++++++++++ ASTNodeDivide.cs | 20 +++ ASTNodeLiteral.cs | 46 +++++++ ASTNodeMultiply.cs | 20 +++ ASTNodeNegative.cs | 37 +++++ ASTNodeSubtract.cs | 20 +++ Coverage.html | 72 +++++++--- ExecutionContext.cs | 12 ++ IASTNode.cs | 16 +++ Lexer.cs | 27 ++-- Parser.cs | 146 ++++++++++++++++++++ ParsingException.cs | 16 +++ SSType.cs | 15 ++ ShintenScript.csproj | 15 ++ ShintenScriptTest/EvaluationTest.cs | 91 ++++++++++++ ShintenScriptTest/LexerTests.cs | 14 +- ShintenScriptTest/ParserTest.cs | 153 +++++++++++++++++++++ ShintenScriptTest/ScaffoldLexer.cs | 27 ++++ ShintenScriptTest/ShintenScriptTest.csproj | 3 + TestOutput.txt | 6 +- Token.cs | 1 + TypeException.cs | 16 +++ 25 files changed, 1044 insertions(+), 32 deletions(-) create mode 100644 ASTNodeAdd.cs create mode 100644 ASTNodeBinaryNumericOperator.cs create mode 100644 ASTNodeBlock.cs create mode 100644 ASTNodeComparison.cs create mode 100644 ASTNodeDivide.cs create mode 100644 ASTNodeLiteral.cs create mode 100644 ASTNodeMultiply.cs create mode 100644 ASTNodeNegative.cs create mode 100644 ASTNodeSubtract.cs create mode 100644 ExecutionContext.cs create mode 100644 IASTNode.cs create mode 100644 Parser.cs create mode 100644 ParsingException.cs create mode 100644 SSType.cs create mode 100644 ShintenScriptTest/EvaluationTest.cs create mode 100644 ShintenScriptTest/ParserTest.cs create mode 100644 ShintenScriptTest/ScaffoldLexer.cs create mode 100644 TypeException.cs diff --git a/ASTNodeAdd.cs b/ASTNodeAdd.cs new file mode 100644 index 0000000..4082ce7 --- /dev/null +++ b/ASTNodeAdd.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShintenScript +{ + public class ASTNodeAdd : ASTNodeBinaryNumericOperator + { + public ASTNodeAdd(IASTNode lhs, IASTNode rhs) : base(lhs, rhs) { } + + public override object CreateFunction11(Func left, Func right) + { + return (Func)(ctx => left(ctx) + right(ctx)); + } + + public override string Op() => "+"; + } +} diff --git a/ASTNodeBinaryNumericOperator.cs b/ASTNodeBinaryNumericOperator.cs new file mode 100644 index 0000000..05636ed --- /dev/null +++ b/ASTNodeBinaryNumericOperator.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShintenScript +{ + public abstract class ASTNodeBinaryNumericOperator : IASTNode + { + public IASTNode lhs, rhs; + + protected ASTNodeBinaryNumericOperator(IASTNode lhs, IASTNode rhs) + { + this.lhs = lhs; + this.rhs = rhs; + } + + public bool Const() + { + return lhs.Const() && rhs.Const(); + } + + public object CreateFunction() + { + SSType lhst = lhs.Type(); + SSType rhst = rhs.Type(); + + if (lhst == SSType.real && rhst == SSType.real) + { + Func left = (Func)lhs.CreateFunction(); + Func right = (Func)rhs.CreateFunction(); + return CreateFunction11(left, right); + } + + throw new Exception("This should be unreachable"); + } + + public SSType Type() + { + SSType lhst = lhs.Type(); + SSType rhst = rhs.Type(); + + if (lhst == SSType.real && rhst == SSType.real) + return SSType.real; + + throw new TypeException($"Unsupported types for operator '{Op()}': {lhst} and {rhst}"); + } + + public abstract string Op(); + public abstract object CreateFunction11(Func left, Func right); + } +} diff --git a/ASTNodeBlock.cs b/ASTNodeBlock.cs new file mode 100644 index 0000000..e0be663 --- /dev/null +++ b/ASTNodeBlock.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShintenScript +{ + public class ASTNodeBlock : IASTNode + { + public IList statements; + + public ASTNodeBlock(IList statements) + { + this.statements = statements; + } + + bool IASTNode.Const() => false; + + object IASTNode.CreateFunction() + { + SSType type = ((IASTNode)this).Type(); + + if (type == SSType.none) + { + List> actions = new List>(); + + foreach (IASTNode statement in statements) + { + object func = statement.CreateFunction(); + if (func is Action action) + actions.Add(action); + else + { + Func ofunc = (Func)func; + actions.Add(ctx => ofunc.DynamicInvoke(ctx)); + } + } + + return (Action)(ctx => + { + foreach (Action action in actions) + action(ctx); + }); + } + + List> actionsm1 = new List>(); + + foreach (IASTNode statement in statements) + { + if (actionsm1.Count == statements.Count - 1) + break; + + object func = statement.CreateFunction(); + if (func is Action action) + actionsm1.Add(action); + else + { + Delegate ofunc = (Delegate)func; + actionsm1.Add(ctx => ofunc.DynamicInvoke(ctx)); + } + } + + if (type == SSType.real) + { + Func final = (Func)statements[statements.Count - 1].CreateFunction(); + return (Func)(ctx => + { + foreach (Action action in actionsm1) + action(ctx); + + return final(ctx); + }); + } + + if (type == SSType.boolean) + { + Func final = (Func)statements[statements.Count - 1].CreateFunction(); + return (Func)(ctx => + { + foreach (Action action in actionsm1) + action(ctx); + + return final(ctx); + }); + } + + throw new Exception("This should be unreachable"); + } + + SSType IASTNode.Type() + { + return statements[statements.Count - 1].Type(); + } + } +} diff --git a/ASTNodeComparison.cs b/ASTNodeComparison.cs new file mode 100644 index 0000000..9672075 --- /dev/null +++ b/ASTNodeComparison.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShintenScript +{ + public class ASTNodeComparison : IASTNode + { + public readonly IList nodes; + public readonly IList comparisons; + + public ASTNodeComparison(IList nodes, IList comparisons) + { + this.nodes = nodes; + this.comparisons = comparisons; + } + + public bool Const() + { + return nodes.All(node => node.Const()); + } + + public object CreateFunction() + { + SSType argType = nodes[0].Type(); + + if (comparisons.Count == 1) + { + if (argType == SSType.boolean) + { + Func left = (Func)nodes[0].CreateFunction(); + Func right = (Func)nodes[1].CreateFunction(); + + switch (comparisons[0]) + { + case Token.Type.EQ: return (Func)(ctx => left(ctx) == right(ctx)); + case Token.Type.NE: return (Func)(ctx => left(ctx) != right(ctx)); + } + } + + if (argType == SSType.real) + { + Func left = (Func)nodes[0].CreateFunction(); + Func right = (Func)nodes[1].CreateFunction(); + + switch (comparisons[0]) + { + case Token.Type.EQ: return (Func)(ctx => left(ctx) == right(ctx)); + case Token.Type.NE: return (Func)(ctx => left(ctx) != right(ctx)); + case Token.Type.LT: return (Func)(ctx => left(ctx) < right(ctx)); + case Token.Type.GT: return (Func)(ctx => left(ctx) > right(ctx)); + case Token.Type.LE: return (Func)(ctx => left(ctx) <= right(ctx)); + case Token.Type.GE: return (Func)(ctx => left(ctx) >= right(ctx)); + } + } + + throw new Exception("This should be unreachable"); + } + + if (argType == SSType.boolean) + { + Func[] args = nodes.Select(node => (Func)node.CreateFunction()).ToArray(); + + return (Func)(ctx => + { + bool against = args[0](ctx); + + for (int i = 1; i < args.Length; i++) + { + bool vs = args[i](ctx); + + if ((against == vs) ^ (comparisons[i - 1] == Token.Type.EQ)) + return false; + + against = vs; + } + + return true; + }); + } + + if (argType == SSType.real) + { + Func[] args = nodes.Select(node => (Func)node.CreateFunction()).ToArray(); + + return (Func)(ctx => + { + float against = args[0](ctx); + + for (int i = 1; i < args.Length; i++) + { + float vs = args[i](ctx); + + switch (comparisons[i - 1]) + { + case Token.Type.EQ: if (vs != against) return false; break; + case Token.Type.NE: if (vs == against) return false; break; + case Token.Type.GT: if (vs >= against) return false; break; + case Token.Type.LT: if (vs <= against) return false; break; + case Token.Type.GE: if (vs > against) return false; break; + case Token.Type.LE: if (vs < against) return false; break; + } + + against = vs; + } + + return true; + }); + } + + throw new Exception("This should be unreachable"); + } + + public SSType Type() + { + bool ord = !comparisons.All(cmp => cmp == Token.Type.EQ || cmp == Token.Type.NE); + + SSType argType = nodes[0].Type(); + + if (argType == SSType.none) + throw new TypeException("Cannot compare null"); + + if (nodes.Skip(1).Any(node => node.Type() != argType)) + throw new TypeException("Cannot compare different types"); + + if (ord && argType != SSType.real) + throw new TypeException($"Type {argType} cannot be ordered"); + + return SSType.boolean; + } + } +} diff --git a/ASTNodeDivide.cs b/ASTNodeDivide.cs new file mode 100644 index 0000000..10fa831 --- /dev/null +++ b/ASTNodeDivide.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShintenScript +{ + public class ASTNodeDivide : ASTNodeBinaryNumericOperator + { + public ASTNodeDivide(IASTNode lhs, IASTNode rhs) : base(lhs, rhs) { } + + public override object CreateFunction11(Func left, Func right) + { + return (Func)(ctx => left(ctx) / right(ctx)); + } + + public override string Op() => "/"; + } +} diff --git a/ASTNodeLiteral.cs b/ASTNodeLiteral.cs new file mode 100644 index 0000000..caf71a5 --- /dev/null +++ b/ASTNodeLiteral.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShintenScript +{ + public class ASTNodeLiteral : IASTNode + { + public readonly object value; + + public ASTNodeLiteral(object value) + { + this.value = value; + } + + public bool Const() => true; + + public object CreateFunction() + { + SSType type = Type(); + + if (type == SSType.none) return (Action)(ctx => { }); + + if (type == SSType.boolean) return (bool)value ? (Func)(ctx => true) : ctx => false; + + if (type == SSType.real) + { + float real = (float)value; + return (Func)(ctx => real); + } + + throw new Exception("This should be unreachable"); + } + + public SSType Type() + { + if (value == null) return SSType.none; + if (value is bool) return SSType.boolean; + if (value is float) return SSType.real; + + throw new Exception("This should be unreachable"); + } + } +} diff --git a/ASTNodeMultiply.cs b/ASTNodeMultiply.cs new file mode 100644 index 0000000..2f2d385 --- /dev/null +++ b/ASTNodeMultiply.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShintenScript +{ + public class ASTNodeMultiply : ASTNodeBinaryNumericOperator + { + public ASTNodeMultiply(IASTNode lhs, IASTNode rhs) : base(lhs, rhs) { } + + public override object CreateFunction11(Func left, Func right) + { + return (Func)(ctx => left(ctx) * right(ctx)); + } + + public override string Op() => "*"; + } +} diff --git a/ASTNodeNegative.cs b/ASTNodeNegative.cs new file mode 100644 index 0000000..4228c2e --- /dev/null +++ b/ASTNodeNegative.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShintenScript +{ + public class ASTNodeNegative : IASTNode + { + public IASTNode node; + + public ASTNodeNegative(IASTNode of) + { + node = of; + } + + public bool Const() + { + return node.Const(); + } + + public object CreateFunction() + { + Func func = (Func)node.CreateFunction(); + return (Func)(ctx => -func(ctx)); + } + + public SSType Type() + { + if (node.Type() != SSType.real) + throw new TypeException($"Unary '-' operator not supported for {node.Type()}"); + + return SSType.real; + } + } +} diff --git a/ASTNodeSubtract.cs b/ASTNodeSubtract.cs new file mode 100644 index 0000000..3d5ebed --- /dev/null +++ b/ASTNodeSubtract.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShintenScript +{ + public class ASTNodeSubtract : ASTNodeBinaryNumericOperator + { + public ASTNodeSubtract(IASTNode lhs, IASTNode rhs) : base(lhs, rhs) { } + + public override object CreateFunction11(Func left, Func right) + { + return (Func)(ctx => left(ctx) - right(ctx)); + } + + public override string Op() => "-"; + } +} diff --git a/Coverage.html b/Coverage.html index 76ecc06..44e5561 100644 --- a/Coverage.html +++ b/Coverage.html @@ -390,19 +390,19 @@ a.percentagebar { -Generated on:1/27/2023 - 10:02:10 AM +Generated on:2/2/2023 - 2:01:44 PM Parser:CoberturaParser Assemblies:2 -Classes:4 -Files:4 -Covered lines:165 -Uncovered lines:50 -Coverable lines:215 -Total lines:355 -Line coverage:76.7% (165 of 215) -Covered branches:44 -Total branches:68 -Branch coverage:64.7% (44 of 68) +Classes:20 +Files:20 +Covered lines:532 +Uncovered lines:188 +Coverable lines:720 +Total lines:1289 +Line coverage:73.8% (532 of 720) +Covered branches:112 +Total branches:232 +Branch coverage:48.2% (112 of 232)

Risk Hotspots

@@ -427,16 +427,32 @@ a.percentagebar { NameCoveredUncoveredCoverableTotalLine coverageCoveredTotalBranch coverage -ShintenScript1135016328869.3%
  
446864.7%
  
-ShintenScript.Lexer1054014521772.4%
  
355070%
  
+ShintenScript32018150193963.8%
  
10321647.6%
  
+ShintenScript.ASTNodeAdd4152080%
  
00
 
+ShintenScript.ASTNodeBinaryNumericOperator205255380%
  
41040%
  
+ShintenScript.ASTNodeBlock3330639652.3%
  
51241.6%
  
+ShintenScript.ASTNodeComparison586911345.4%
  
0640%
 
+ShintenScript.ASTNodeDivide4152080%
  
00
 
+ShintenScript.ASTNodeLiteral183214685.7%
  
102050%
  
+ShintenScript.ASTNodeMultiply4152080%
  
00
 
+ShintenScript.ASTNodeNegative124163775%
  
1250%
  
+ShintenScript.ASTNodeSubtract4152080%
  
00
 
+ShintenScript.Lexer1322015222886.8%
  
516282.2%
  
ShintenScript.LexingException033160%
 
00
 
-ShintenScript.Token87155553.3%
  
91850%
  
-ShintenScriptTest5205267100%
 
00
 
-ShintenScriptTest.LexerTests5205267100%
 
00
 
+ShintenScript.Parser73138614684.8%
  
232882.1%
  
+ShintenScript.ParsingException033160%
 
00
 
+ShintenScript.SSType30315100%
 
00
 
+ShintenScript.Token87155653.3%
  
91850%
  
+ShintenScript.TypeException033160%
 
00
 
+ShintenScriptTest212721935096.8%
  
91656.2%
  
+ShintenScriptTest.EvaluationTest4104191100%
 
00
 
+ShintenScriptTest.LexerTests6006079100%
 
00
 
+ShintenScriptTest.ParserTest102710915393.5%
  
71450%
  
+ShintenScriptTest.ScaffoldLexer90927100%
 
22100%
 
- +