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%
 
- +