Compare commits

...

2 Commits

Author SHA1 Message Date
Sen ec8ff367c0 Merge branch 'master' of git.touhou.dev:StellatedCUBE/ShintenScript into master 2023-02-02 14:06:16 +00:00
Sen abbd4a74ef Parser stuff 2023-02-02 14:05:53 +00:00
25 changed files with 1044 additions and 32 deletions

20
ASTNodeAdd.cs Normal file
View File

@ -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<ExecutionContext, float> left, Func<ExecutionContext, float> right)
{
return (Func<ExecutionContext, float>)(ctx => left(ctx) + right(ctx));
}
public override string Op() => "+";
}
}

View File

@ -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<ExecutionContext, float> left = (Func<ExecutionContext, float>)lhs.CreateFunction();
Func<ExecutionContext, float> right = (Func<ExecutionContext, float>)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<ExecutionContext, float> left, Func<ExecutionContext, float> right);
}
}

96
ASTNodeBlock.cs Normal file
View File

@ -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<IASTNode> statements;
public ASTNodeBlock(IList<IASTNode> statements)
{
this.statements = statements;
}
bool IASTNode.Const() => false;
object IASTNode.CreateFunction()
{
SSType type = ((IASTNode)this).Type();
if (type == SSType.none)
{
List<Action<ExecutionContext>> actions = new List<Action<ExecutionContext>>();
foreach (IASTNode statement in statements)
{
object func = statement.CreateFunction();
if (func is Action<ExecutionContext> action)
actions.Add(action);
else
{
Func<ExecutionContext, object> ofunc = (Func<ExecutionContext, object>)func;
actions.Add(ctx => ofunc.DynamicInvoke(ctx));
}
}
return (Action<ExecutionContext>)(ctx =>
{
foreach (Action<ExecutionContext> action in actions)
action(ctx);
});
}
List<Action<ExecutionContext>> actionsm1 = new List<Action<ExecutionContext>>();
foreach (IASTNode statement in statements)
{
if (actionsm1.Count == statements.Count - 1)
break;
object func = statement.CreateFunction();
if (func is Action<ExecutionContext> action)
actionsm1.Add(action);
else
{
Delegate ofunc = (Delegate)func;
actionsm1.Add(ctx => ofunc.DynamicInvoke(ctx));
}
}
if (type == SSType.real)
{
Func<ExecutionContext, float> final = (Func<ExecutionContext, float>)statements[statements.Count - 1].CreateFunction();
return (Func<ExecutionContext, float>)(ctx =>
{
foreach (Action<ExecutionContext> action in actionsm1)
action(ctx);
return final(ctx);
});
}
if (type == SSType.boolean)
{
Func<ExecutionContext, bool> final = (Func<ExecutionContext, bool>)statements[statements.Count - 1].CreateFunction();
return (Func<ExecutionContext, bool>)(ctx =>
{
foreach (Action<ExecutionContext> action in actionsm1)
action(ctx);
return final(ctx);
});
}
throw new Exception("This should be unreachable");
}
SSType IASTNode.Type()
{
return statements[statements.Count - 1].Type();
}
}
}

134
ASTNodeComparison.cs Normal file
View File

@ -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<IASTNode> nodes;
public readonly IList<Token.Type> comparisons;
public ASTNodeComparison(IList<IASTNode> nodes, IList<Token.Type> 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<ExecutionContext, bool> left = (Func<ExecutionContext, bool>)nodes[0].CreateFunction();
Func<ExecutionContext, bool> right = (Func<ExecutionContext, bool>)nodes[1].CreateFunction();
switch (comparisons[0])
{
case Token.Type.EQ: return (Func<ExecutionContext, bool>)(ctx => left(ctx) == right(ctx));
case Token.Type.NE: return (Func<ExecutionContext, bool>)(ctx => left(ctx) != right(ctx));
}
}
if (argType == SSType.real)
{
Func<ExecutionContext, float> left = (Func<ExecutionContext, float>)nodes[0].CreateFunction();
Func<ExecutionContext, float> right = (Func<ExecutionContext, float>)nodes[1].CreateFunction();
switch (comparisons[0])
{
case Token.Type.EQ: return (Func<ExecutionContext, bool>)(ctx => left(ctx) == right(ctx));
case Token.Type.NE: return (Func<ExecutionContext, bool>)(ctx => left(ctx) != right(ctx));
case Token.Type.LT: return (Func<ExecutionContext, bool>)(ctx => left(ctx) < right(ctx));
case Token.Type.GT: return (Func<ExecutionContext, bool>)(ctx => left(ctx) > right(ctx));
case Token.Type.LE: return (Func<ExecutionContext, bool>)(ctx => left(ctx) <= right(ctx));
case Token.Type.GE: return (Func<ExecutionContext, bool>)(ctx => left(ctx) >= right(ctx));
}
}
throw new Exception("This should be unreachable");
}
if (argType == SSType.boolean)
{
Func<ExecutionContext, bool>[] args = nodes.Select(node => (Func<ExecutionContext, bool>)node.CreateFunction()).ToArray();
return (Func<ExecutionContext, bool>)(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<ExecutionContext, float>[] args = nodes.Select(node => (Func<ExecutionContext, float>)node.CreateFunction()).ToArray();
return (Func<ExecutionContext, bool>)(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;
}
}
}

20
ASTNodeDivide.cs Normal file
View File

@ -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<ExecutionContext, float> left, Func<ExecutionContext, float> right)
{
return (Func<ExecutionContext, float>)(ctx => left(ctx) / right(ctx));
}
public override string Op() => "/";
}
}

46
ASTNodeLiteral.cs Normal file
View File

@ -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<ExecutionContext>)(ctx => { });
if (type == SSType.boolean) return (bool)value ? (Func<ExecutionContext, bool>)(ctx => true) : ctx => false;
if (type == SSType.real)
{
float real = (float)value;
return (Func<ExecutionContext, float>)(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");
}
}
}

20
ASTNodeMultiply.cs Normal file
View File

@ -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<ExecutionContext, float> left, Func<ExecutionContext, float> right)
{
return (Func<ExecutionContext, float>)(ctx => left(ctx) * right(ctx));
}
public override string Op() => "*";
}
}

37
ASTNodeNegative.cs Normal file
View File

@ -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<ExecutionContext, float> func = (Func<ExecutionContext, float>)node.CreateFunction();
return (Func<ExecutionContext, float>)(ctx => -func(ctx));
}
public SSType Type()
{
if (node.Type() != SSType.real)
throw new TypeException($"Unary '-' operator not supported for {node.Type()}");
return SSType.real;
}
}
}

20
ASTNodeSubtract.cs Normal file
View File

@ -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<ExecutionContext, float> left, Func<ExecutionContext, float> right)
{
return (Func<ExecutionContext, float>)(ctx => left(ctx) - right(ctx));
}
public override string Op() => "-";
}
}

View File

@ -390,19 +390,19 @@ a.percentagebar {
<col /> <col />
</colgroup> </colgroup>
<tbody> <tbody>
<tr><th>Generated on:</th><td>1/27/2023 - 10:02:10 AM</td></tr> <tr><th>Generated on:</th><td>2/2/2023 - 2:01:44 PM</td></tr>
<tr><th>Parser:</th><td>CoberturaParser</td></tr> <tr><th>Parser:</th><td>CoberturaParser</td></tr>
<tr><th>Assemblies:</th><td>2</td></tr> <tr><th>Assemblies:</th><td>2</td></tr>
<tr><th>Classes:</th><td>4</td></tr> <tr><th>Classes:</th><td>20</td></tr>
<tr><th>Files:</th><td>4</td></tr> <tr><th>Files:</th><td>20</td></tr>
<tr><th>Covered lines:</th><td>165</td></tr> <tr><th>Covered lines:</th><td>532</td></tr>
<tr><th>Uncovered lines:</th><td>50</td></tr> <tr><th>Uncovered lines:</th><td>188</td></tr>
<tr><th>Coverable lines:</th><td>215</td></tr> <tr><th>Coverable lines:</th><td>720</td></tr>
<tr><th>Total lines:</th><td>355</td></tr> <tr><th>Total lines:</th><td>1289</td></tr>
<tr><th>Line coverage:</th><td>76.7% (165 of 215)</td></tr> <tr><th>Line coverage:</th><td>73.8% (532 of 720)</td></tr>
<tr><th>Covered branches:</th><td>44</td></tr> <tr><th>Covered branches:</th><td>112</td></tr>
<tr><th>Total branches:</th><td>68</td></tr> <tr><th>Total branches:</th><td>232</td></tr>
<tr><th>Branch coverage:</th><td>64.7% (44 of 68)</td></tr> <tr><th>Branch coverage:</th><td>48.2% (112 of 232)</td></tr>
</tbody> </tbody>
</table> </table>
<h1>Risk Hotspots</h1> <h1>Risk Hotspots</h1>
@ -427,16 +427,32 @@ a.percentagebar {
</colgroup> </colgroup>
<thead><tr><th>Name</th><th class="right">Covered</th><th class="right">Uncovered</th><th class="right">Coverable</th><th class="right">Total</th><th class="center" colspan="2">Line coverage</th><th class="right">Covered</th><th class="right">Total</th><th class="center" colspan="2">Branch coverage</th></tr></thead> <thead><tr><th>Name</th><th class="right">Covered</th><th class="right">Uncovered</th><th class="right">Coverable</th><th class="right">Total</th><th class="center" colspan="2">Line coverage</th><th class="right">Covered</th><th class="right">Total</th><th class="center" colspan="2">Branch coverage</th></tr></thead>
<tbody> <tbody>
<tr><th>ShintenScript</th><th class="right">113</th><th class="right">50</th><th class="right">163</th><th class="right">288</th><th title="LineCoverage: 113/163" class="right">69.3%</th><th><table class="coverage"><tr><td class="green covered69">&nbsp;</td><td class="red covered31">&nbsp;</td></tr></table></th><th class="right">44</th><th class="right">68</th><th class="right" title="44/68">64.7%</th><th><table class="coverage"><tr><td class="green covered65">&nbsp;</td><td class="red covered35">&nbsp;</td></tr></table></th></tr> <tr><th>ShintenScript</th><th class="right">320</th><th class="right">181</th><th class="right">501</th><th class="right">939</th><th title="LineCoverage: 320/501" class="right">63.8%</th><th><table class="coverage"><tr><td class="green covered64">&nbsp;</td><td class="red covered36">&nbsp;</td></tr></table></th><th class="right">103</th><th class="right">216</th><th class="right" title="103/216">47.6%</th><th><table class="coverage"><tr><td class="green covered48">&nbsp;</td><td class="red covered52">&nbsp;</td></tr></table></th></tr>
<tr><td><a href="ShintenScript_Lexer.html">ShintenScript.Lexer</a></td><td class="right">105</td><td class="right">40</td><td class="right">145</td><td class="right">217</td><td title="LineCoverage: 105/145" class="right">72.4%</td><td><table class="coverage"><tr><td class="green covered72">&nbsp;</td><td class="red covered28">&nbsp;</td></tr></table></td><td class="right">35</td><td class="right">50</td><td class="right" title="35/50">70%</td><td><table class="coverage"><tr><td class="green covered70">&nbsp;</td><td class="red covered30">&nbsp;</td></tr></table></td></tr> <tr><td><a href="ShintenScript_ASTNodeAdd.html">ShintenScript.ASTNodeAdd</a></td><td class="right">4</td><td class="right">1</td><td class="right">5</td><td class="right">20</td><td title="LineCoverage: 4/5" class="right">80%</td><td><table class="coverage"><tr><td class="green covered80">&nbsp;</td><td class="red covered20">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_ASTNodeBinaryNumericOperator.html">ShintenScript.ASTNodeBinaryNumericOperator</a></td><td class="right">20</td><td class="right">5</td><td class="right">25</td><td class="right">53</td><td title="LineCoverage: 20/25" class="right">80%</td><td><table class="coverage"><tr><td class="green covered80">&nbsp;</td><td class="red covered20">&nbsp;</td></tr></table></td><td class="right">4</td><td class="right">10</td><td class="right" title="4/10">40%</td><td><table class="coverage"><tr><td class="green covered40">&nbsp;</td><td class="red covered60">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_ASTNodeBlock.html">ShintenScript.ASTNodeBlock</a></td><td class="right">33</td><td class="right">30</td><td class="right">63</td><td class="right">96</td><td title="LineCoverage: 33/63" class="right">52.3%</td><td><table class="coverage"><tr><td class="green covered52">&nbsp;</td><td class="red covered48">&nbsp;</td></tr></table></td><td class="right">5</td><td class="right">12</td><td class="right" title="5/12">41.6%</td><td><table class="coverage"><tr><td class="green covered42">&nbsp;</td><td class="red covered58">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_ASTNodeComparison.html">ShintenScript.ASTNodeComparison</a></td><td class="right">5</td><td class="right">86</td><td class="right">91</td><td class="right">134</td><td title="LineCoverage: 5/91" class="right">5.4%</td><td><table class="coverage"><tr><td class="green covered5">&nbsp;</td><td class="red covered95">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">64</td><td class="right" title="0/64">0%</td><td><table class="coverage"><tr><td class="red covered100">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_ASTNodeDivide.html">ShintenScript.ASTNodeDivide</a></td><td class="right">4</td><td class="right">1</td><td class="right">5</td><td class="right">20</td><td title="LineCoverage: 4/5" class="right">80%</td><td><table class="coverage"><tr><td class="green covered80">&nbsp;</td><td class="red covered20">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_ASTNodeLiteral.html">ShintenScript.ASTNodeLiteral</a></td><td class="right">18</td><td class="right">3</td><td class="right">21</td><td class="right">46</td><td title="LineCoverage: 18/21" class="right">85.7%</td><td><table class="coverage"><tr><td class="green covered86">&nbsp;</td><td class="red covered14">&nbsp;</td></tr></table></td><td class="right">10</td><td class="right">20</td><td class="right" title="10/20">50%</td><td><table class="coverage"><tr><td class="green covered50">&nbsp;</td><td class="red covered50">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_ASTNodeMultiply.html">ShintenScript.ASTNodeMultiply</a></td><td class="right">4</td><td class="right">1</td><td class="right">5</td><td class="right">20</td><td title="LineCoverage: 4/5" class="right">80%</td><td><table class="coverage"><tr><td class="green covered80">&nbsp;</td><td class="red covered20">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_ASTNodeNegative.html">ShintenScript.ASTNodeNegative</a></td><td class="right">12</td><td class="right">4</td><td class="right">16</td><td class="right">37</td><td title="LineCoverage: 12/16" class="right">75%</td><td><table class="coverage"><tr><td class="green covered75">&nbsp;</td><td class="red covered25">&nbsp;</td></tr></table></td><td class="right">1</td><td class="right">2</td><td class="right" title="1/2">50%</td><td><table class="coverage"><tr><td class="green covered50">&nbsp;</td><td class="red covered50">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_ASTNodeSubtract.html">ShintenScript.ASTNodeSubtract</a></td><td class="right">4</td><td class="right">1</td><td class="right">5</td><td class="right">20</td><td title="LineCoverage: 4/5" class="right">80%</td><td><table class="coverage"><tr><td class="green covered80">&nbsp;</td><td class="red covered20">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_Lexer.html">ShintenScript.Lexer</a></td><td class="right">132</td><td class="right">20</td><td class="right">152</td><td class="right">228</td><td title="LineCoverage: 132/152" class="right">86.8%</td><td><table class="coverage"><tr><td class="green covered87">&nbsp;</td><td class="red covered13">&nbsp;</td></tr></table></td><td class="right">51</td><td class="right">62</td><td class="right" title="51/62">82.2%</td><td><table class="coverage"><tr><td class="green covered82">&nbsp;</td><td class="red covered18">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_LexingException.html">ShintenScript.LexingException</a></td><td class="right">0</td><td class="right">3</td><td class="right">3</td><td class="right">16</td><td title="LineCoverage: 0/3" class="right">0%</td><td><table class="coverage"><tr><td class="red covered100">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr> <tr><td><a href="ShintenScript_LexingException.html">ShintenScript.LexingException</a></td><td class="right">0</td><td class="right">3</td><td class="right">3</td><td class="right">16</td><td title="LineCoverage: 0/3" class="right">0%</td><td><table class="coverage"><tr><td class="red covered100">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_Token.html">ShintenScript.Token</a></td><td class="right">8</td><td class="right">7</td><td class="right">15</td><td class="right">55</td><td title="LineCoverage: 8/15" class="right">53.3%</td><td><table class="coverage"><tr><td class="green covered53">&nbsp;</td><td class="red covered47">&nbsp;</td></tr></table></td><td class="right">9</td><td class="right">18</td><td class="right" title="9/18">50%</td><td><table class="coverage"><tr><td class="green covered50">&nbsp;</td><td class="red covered50">&nbsp;</td></tr></table></td></tr> <tr><td><a href="ShintenScript_Parser.html">ShintenScript.Parser</a></td><td class="right">73</td><td class="right">13</td><td class="right">86</td><td class="right">146</td><td title="LineCoverage: 73/86" class="right">84.8%</td><td><table class="coverage"><tr><td class="green covered85">&nbsp;</td><td class="red covered15">&nbsp;</td></tr></table></td><td class="right">23</td><td class="right">28</td><td class="right" title="23/28">82.1%</td><td><table class="coverage"><tr><td class="green covered82">&nbsp;</td><td class="red covered18">&nbsp;</td></tr></table></td></tr>
<tr><th>ShintenScriptTest</th><th class="right">52</th><th class="right">0</th><th class="right">52</th><th class="right">67</th><th title="LineCoverage: 52/52" class="right">100%</th><th><table class="coverage"><tr><td class="green covered100">&nbsp;</td></tr></table></th><th class="right">0</th><th class="right">0</th><th class="right" title="-"></th><th><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></th></tr> <tr><td><a href="ShintenScript_ParsingException.html">ShintenScript.ParsingException</a></td><td class="right">0</td><td class="right">3</td><td class="right">3</td><td class="right">16</td><td title="LineCoverage: 0/3" class="right">0%</td><td><table class="coverage"><tr><td class="red covered100">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScriptTest_LexerTests.html">ShintenScriptTest.LexerTests</a></td><td class="right">52</td><td class="right">0</td><td class="right">52</td><td class="right">67</td><td title="LineCoverage: 52/52" class="right">100%</td><td><table class="coverage"><tr><td class="green covered100">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr> <tr><td><a href="ShintenScript_SSType.html">ShintenScript.SSType</a></td><td class="right">3</td><td class="right">0</td><td class="right">3</td><td class="right">15</td><td title="LineCoverage: 3/3" class="right">100%</td><td><table class="coverage"><tr><td class="green covered100">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_Token.html">ShintenScript.Token</a></td><td class="right">8</td><td class="right">7</td><td class="right">15</td><td class="right">56</td><td title="LineCoverage: 8/15" class="right">53.3%</td><td><table class="coverage"><tr><td class="green covered53">&nbsp;</td><td class="red covered47">&nbsp;</td></tr></table></td><td class="right">9</td><td class="right">18</td><td class="right" title="9/18">50%</td><td><table class="coverage"><tr><td class="green covered50">&nbsp;</td><td class="red covered50">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScript_TypeException.html">ShintenScript.TypeException</a></td><td class="right">0</td><td class="right">3</td><td class="right">3</td><td class="right">16</td><td title="LineCoverage: 0/3" class="right">0%</td><td><table class="coverage"><tr><td class="red covered100">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr>
<tr><th>ShintenScriptTest</th><th class="right">212</th><th class="right">7</th><th class="right">219</th><th class="right">350</th><th title="LineCoverage: 212/219" class="right">96.8%</th><th><table class="coverage"><tr><td class="green covered97">&nbsp;</td><td class="red covered3">&nbsp;</td></tr></table></th><th class="right">9</th><th class="right">16</th><th class="right" title="9/16">56.2%</th><th><table class="coverage"><tr><td class="green covered56">&nbsp;</td><td class="red covered44">&nbsp;</td></tr></table></th></tr>
<tr><td><a href="ShintenScriptTest_EvaluationTest.html">ShintenScriptTest.EvaluationTest</a></td><td class="right">41</td><td class="right">0</td><td class="right">41</td><td class="right">91</td><td title="LineCoverage: 41/41" class="right">100%</td><td><table class="coverage"><tr><td class="green covered100">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScriptTest_LexerTests.html">ShintenScriptTest.LexerTests</a></td><td class="right">60</td><td class="right">0</td><td class="right">60</td><td class="right">79</td><td title="LineCoverage: 60/60" class="right">100%</td><td><table class="coverage"><tr><td class="green covered100">&nbsp;</td></tr></table></td><td class="right">0</td><td class="right">0</td><td class="right" title="-"></td><td><table class="coverage"><tr><td class="gray covered100">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScriptTest_ParserTest.html">ShintenScriptTest.ParserTest</a></td><td class="right">102</td><td class="right">7</td><td class="right">109</td><td class="right">153</td><td title="LineCoverage: 102/109" class="right">93.5%</td><td><table class="coverage"><tr><td class="green covered94">&nbsp;</td><td class="red covered6">&nbsp;</td></tr></table></td><td class="right">7</td><td class="right">14</td><td class="right" title="7/14">50%</td><td><table class="coverage"><tr><td class="green covered50">&nbsp;</td><td class="red covered50">&nbsp;</td></tr></table></td></tr>
<tr><td><a href="ShintenScriptTest_ScaffoldLexer.html">ShintenScriptTest.ScaffoldLexer</a></td><td class="right">9</td><td class="right">0</td><td class="right">9</td><td class="right">27</td><td title="LineCoverage: 9/9" class="right">100%</td><td><table class="coverage"><tr><td class="green covered100">&nbsp;</td></tr></table></td><td class="right">2</td><td class="right">2</td><td class="right" title="2/2">100%</td><td><table class="coverage"><tr><td class="green covered100">&nbsp;</td></tr></table></td></tr>
</tbody> </tbody>
</table> </table>
</coverage-info> </coverage-info>
<div class="footer">Generated by: ReportGenerator 4.7.1.0<br />1/27/2023 - 10:02:10 AM<br /><a href="https://github.com/danielpalme/ReportGenerator">GitHub</a> | <a href="http://www.palmmedia.de">www.palmmedia.de</a></div></div></div> <div class="footer">Generated by: ReportGenerator 4.7.1.0<br />2/2/2023 - 2:01:44 PM<br /><a href="https://github.com/danielpalme/ReportGenerator">GitHub</a> | <a href="http://www.palmmedia.de">www.palmmedia.de</a></div></div></div>
<script type="text/javascript">/* <![CDATA[ */ /* Chartist.js 0.11.0 <script type="text/javascript">/* <![CDATA[ */ /* Chartist.js 0.11.0
* Copyright © 2017 Gion Kunz * Copyright © 2017 Gion Kunz
* Free to use under either the WTFPL license or the MIT license. * Free to use under either the WTFPL license or the MIT license.
@ -663,14 +679,30 @@ var assemblies = [
{ {
"name": "ShintenScript", "name": "ShintenScript",
"classes": [ "classes": [
{ "name": "ShintenScript.Lexer", "rp": "ShintenScript_Lexer.html", "cl": 105, "ucl": 40, "cal": 145, "tl": 217, "ct": "LineCoverage", "mc": "-", "cb": 35, "tb": 50, "lch": [], "bch": [], "hc": [] }, { "name": "ShintenScript.ASTNodeAdd", "rp": "ShintenScript_ASTNodeAdd.html", "cl": 4, "ucl": 1, "cal": 5, "tl": 20, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.ASTNodeBinaryNumericOperator", "rp": "ShintenScript_ASTNodeBinaryNumericOperator.html", "cl": 20, "ucl": 5, "cal": 25, "tl": 53, "ct": "LineCoverage", "mc": "-", "cb": 4, "tb": 10, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.ASTNodeBlock", "rp": "ShintenScript_ASTNodeBlock.html", "cl": 33, "ucl": 30, "cal": 63, "tl": 96, "ct": "LineCoverage", "mc": "-", "cb": 5, "tb": 12, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.ASTNodeComparison", "rp": "ShintenScript_ASTNodeComparison.html", "cl": 5, "ucl": 86, "cal": 91, "tl": 134, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 64, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.ASTNodeDivide", "rp": "ShintenScript_ASTNodeDivide.html", "cl": 4, "ucl": 1, "cal": 5, "tl": 20, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.ASTNodeLiteral", "rp": "ShintenScript_ASTNodeLiteral.html", "cl": 18, "ucl": 3, "cal": 21, "tl": 46, "ct": "LineCoverage", "mc": "-", "cb": 10, "tb": 20, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.ASTNodeMultiply", "rp": "ShintenScript_ASTNodeMultiply.html", "cl": 4, "ucl": 1, "cal": 5, "tl": 20, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.ASTNodeNegative", "rp": "ShintenScript_ASTNodeNegative.html", "cl": 12, "ucl": 4, "cal": 16, "tl": 37, "ct": "LineCoverage", "mc": "-", "cb": 1, "tb": 2, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.ASTNodeSubtract", "rp": "ShintenScript_ASTNodeSubtract.html", "cl": 4, "ucl": 1, "cal": 5, "tl": 20, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.Lexer", "rp": "ShintenScript_Lexer.html", "cl": 132, "ucl": 20, "cal": 152, "tl": 228, "ct": "LineCoverage", "mc": "-", "cb": 51, "tb": 62, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.LexingException", "rp": "ShintenScript_LexingException.html", "cl": 0, "ucl": 3, "cal": 3, "tl": 16, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] }, { "name": "ShintenScript.LexingException", "rp": "ShintenScript_LexingException.html", "cl": 0, "ucl": 3, "cal": 3, "tl": 16, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.Token", "rp": "ShintenScript_Token.html", "cl": 8, "ucl": 7, "cal": 15, "tl": 55, "ct": "LineCoverage", "mc": "-", "cb": 9, "tb": 18, "lch": [], "bch": [], "hc": [] }, { "name": "ShintenScript.Parser", "rp": "ShintenScript_Parser.html", "cl": 73, "ucl": 13, "cal": 86, "tl": 146, "ct": "LineCoverage", "mc": "-", "cb": 23, "tb": 28, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.ParsingException", "rp": "ShintenScript_ParsingException.html", "cl": 0, "ucl": 3, "cal": 3, "tl": 16, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.SSType", "rp": "ShintenScript_SSType.html", "cl": 3, "ucl": 0, "cal": 3, "tl": 15, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.Token", "rp": "ShintenScript_Token.html", "cl": 8, "ucl": 7, "cal": 15, "tl": 56, "ct": "LineCoverage", "mc": "-", "cb": 9, "tb": 18, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScript.TypeException", "rp": "ShintenScript_TypeException.html", "cl": 0, "ucl": 3, "cal": 3, "tl": 16, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] },
]}, ]},
{ {
"name": "ShintenScriptTest", "name": "ShintenScriptTest",
"classes": [ "classes": [
{ "name": "ShintenScriptTest.LexerTests", "rp": "ShintenScriptTest_LexerTests.html", "cl": 52, "ucl": 0, "cal": 52, "tl": 67, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] }, { "name": "ShintenScriptTest.EvaluationTest", "rp": "ShintenScriptTest_EvaluationTest.html", "cl": 41, "ucl": 0, "cal": 41, "tl": 91, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScriptTest.LexerTests", "rp": "ShintenScriptTest_LexerTests.html", "cl": 60, "ucl": 0, "cal": 60, "tl": 79, "ct": "LineCoverage", "mc": "-", "cb": 0, "tb": 0, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScriptTest.ParserTest", "rp": "ShintenScriptTest_ParserTest.html", "cl": 102, "ucl": 7, "cal": 109, "tl": 153, "ct": "LineCoverage", "mc": "-", "cb": 7, "tb": 14, "lch": [], "bch": [], "hc": [] },
{ "name": "ShintenScriptTest.ScaffoldLexer", "rp": "ShintenScriptTest_ScaffoldLexer.html", "cl": 9, "ucl": 0, "cal": 9, "tl": 27, "ct": "LineCoverage", "mc": "-", "cb": 2, "tb": 2, "lch": [], "bch": [], "hc": [] },
]}, ]},
]; ];

12
ExecutionContext.cs Normal file
View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShintenScript
{
public class ExecutionContext
{
}
}

16
IASTNode.cs Normal file
View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShintenScript
{
public interface IASTNode
{
bool Const();
SSType Type();
//IASTNode OptPass1();
object CreateFunction();
}
}

View File

@ -20,6 +20,7 @@ namespace ShintenScript
("*=", Token.Type.ASTERISKASSIGN), ("*=", Token.Type.ASTERISKASSIGN),
("/=", Token.Type.SLASHASSIGN), ("/=", Token.Type.SLASHASSIGN),
(";", Token.Type.SEMICOLON),
("+", Token.Type.PLUS), ("+", Token.Type.PLUS),
("-", Token.Type.MINUS), ("-", Token.Type.MINUS),
("*", Token.Type.ASTERISK), ("*", Token.Type.ASTERISK),
@ -51,7 +52,7 @@ namespace ShintenScript
Token current = new Token { type = Token.Type.NULL }; Token current = new Token { type = Token.Type.NULL };
Token ParseOne() public virtual Token ParseOne()
{ {
if (EOF()) if (EOF())
return new Token { type = Token.Type.EOF }; return new Token { type = Token.Type.EOF };
@ -77,7 +78,7 @@ namespace ShintenScript
{ {
StringBuilder identifierBuilder = new StringBuilder(); StringBuilder identifierBuilder = new StringBuilder();
while (char.IsLetterOrDigit(code[index])) while (!EOF() && char.IsLetterOrDigit(code[index]))
{ {
identifierBuilder.Append(code[index]); identifierBuilder.Append(code[index]);
index++; index++;
@ -97,10 +98,15 @@ namespace ShintenScript
if (char.IsNumber(code[index])) if (char.IsNumber(code[index]))
{ {
StringBuilder numberBuilder = new StringBuilder(); StringBuilder numberBuilder = new StringBuilder();
bool seenLetter = false;
while (char.IsLetterOrDigit(code[index]) || code[index] == '.' || (code[index] == '-' && (code[index - 1] == 'e' || code[index - 1] == 'E'))) while (!EOF() && (char.IsLetterOrDigit(code[index]) || code[index] == '.' || (!seenLetter && code[index] == '-' && (code[index - 1] == 'e' || code[index - 1] == 'E'))))
{ {
numberBuilder.Append(code[index]); numberBuilder.Append(code[index]);
if (char.IsLetter(code[index]) && code[index] != 'e' && code[index] != 'E')
seenLetter = true;
index++; index++;
} }
@ -187,24 +193,29 @@ namespace ShintenScript
if (types.Length == 1) if (types.Length == 1)
{ {
throw new LexingException($"Encountered {token.type} when expecting {types[0]}"); throw new ParsingException($"Encountered {token.type} when expecting {types[0]}");
} }
else if (types.Length == 2) else if (types.Length == 2)
{ {
throw new LexingException($"Encountered {token.type} when expecting one of {types[0]} or {types[1]}"); throw new ParsingException($"Encountered {token.type} when expecting one of {types[0]} or {types[1]}");
} }
else else
{ {
string list = string.Join(", ", types.Take(types.Length - 1)); string list = string.Join(", ", types.Take(types.Length - 1));
throw new LexingException($"Encountered {token.type} when expecting one of {list}, or {types[types.Length - 1]}"); throw new ParsingException($"Encountered {token.type} when expecting one of {list}, or {types[types.Length - 1]}");
} }
} }
public IEnumerable<Token> TokenStream() public IEnumerable<Token> TokenStream()
{ {
while (!EOF()) while (true)
{ {
yield return Pop(); Token token = Pop();
if (token.type == Token.Type.EOF)
break;
yield return token;
} }
} }

146
Parser.cs Normal file
View File

@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShintenScript
{
public class Parser
{
protected readonly Lexer lexer;
public Parser(Lexer lexer)
{
this.lexer = lexer;
}
public IASTNode ParseExpression()
{
if (lexer.Accept(Token.Type.SEMICOLON))
{
lexer.Pop();
return new ASTNodeLiteral(null);
}
return ParseExpressionAssignLevel();
}
public IASTNode ParseExpressionAssignLevel()
{
IASTNode lhs = ParseExpressionComparisonLevel();
return lhs;
}
public IASTNode ParseExpressionComparisonLevel()
{
List<IASTNode> nodes = new List<IASTNode>();
List<Token.Type> comparisons = new List<Token.Type>();
nodes.Add(ParseExpressionAdditiveLevel());
while (lexer.Accept(Token.Type.EQ, Token.Type.NE, Token.Type.LT, Token.Type.GT, Token.Type.LE, Token.Type.GE))
{
comparisons.Add(lexer.Pop().type);
nodes.Add(ParseExpressionAdditiveLevel());
}
if (nodes.Count == 1)
return nodes[0];
return new ASTNodeComparison(nodes, comparisons);
}
public IASTNode ParseExpressionAdditiveLevel()
{
IASTNode lhs = ParseExpressionMultiplicativeLevel();
if (lexer.Accept(Token.Type.PLUS, Token.Type.MINUS))
{
bool isAddition = lexer.Pop().type == Token.Type.PLUS;
IASTNode rhs = ParseExpressionAdditiveLevel();
if (isAddition)
return new ASTNodeAdd(lhs, rhs);
else
return new ASTNodeSubtract(lhs, rhs);
}
return lhs;
}
public IASTNode ParseExpressionMultiplicativeLevel()
{
IASTNode lhs = ParseExpressionWithPrefix();
if (lexer.Accept(Token.Type.ASTERISK, Token.Type.SLASH))
{
bool isMultiplication = lexer.Pop().type == Token.Type.ASTERISK;
IASTNode rhs = ParseExpressionMultiplicativeLevel();
if (isMultiplication)
return new ASTNodeMultiply(lhs, rhs);
else
return new ASTNodeDivide(lhs, rhs);
}
return lhs;
}
public IASTNode ParseExpressionWithPrefix()
{
if (lexer.Accept(Token.Type.PLUS))
lexer.Pop();
if (lexer.Accept(Token.Type.MINUS))
{
lexer.Pop();
return new ASTNodeNegative(ParseExpressionPrimary());
}
return ParseExpressionPrimary();
}
public IASTNode ParseExpressionPrimary()
{
if (lexer.Accept(Token.Type.NUMBER))
return new ASTNodeLiteral(lexer.Pop().data);
if (lexer.Accept(Token.Type.LPAREN))
return ParseExpressionParenthesised();
if (lexer.Accept(Token.Type.LBRACE))
return ParseBlock();
throw new ParsingException("Expected expression");
}
public IASTNode ParseExpressionParenthesised()
{
lexer.Expect(Token.Type.LPAREN);
IASTNode expression = ParseExpression();
lexer.Expect(Token.Type.RPAREN);
return expression;
}
public IASTNode ParseBlock()
{
lexer.Expect(Token.Type.LBRACE);
List<IASTNode> statements = new List<IASTNode>();
while (!lexer.Accept(Token.Type.RBRACE))
{
statements.Add(ParseExpression());
}
lexer.Pop();
if (statements.Count == 0)
return new ASTNodeLiteral(null);
return new ASTNodeBlock(statements);
}
}
}

16
ParsingException.cs Normal file
View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShintenScript
{
public class ParsingException : Exception
{
public ParsingException(string message) : base(message)
{
}
}
}

15
SSType.cs Normal file
View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShintenScript
{
public class SSType
{
public static readonly SSType none = new SSType();
public static readonly SSType real = new SSType();
public static readonly SSType boolean = new SSType();
}
}

View File

@ -41,10 +41,25 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ASTNodeAdd.cs" />
<Compile Include="ASTNodeBinaryNumericOperator.cs" />
<Compile Include="ASTNodeBlock.cs" />
<Compile Include="ASTNodeComparison.cs" />
<Compile Include="ASTNodeDivide.cs" />
<Compile Include="ASTNodeLiteral.cs" />
<Compile Include="ASTNodeMultiply.cs" />
<Compile Include="ASTNodeNegative.cs" />
<Compile Include="ASTNodeSubtract.cs" />
<Compile Include="ExecutionContext.cs" />
<Compile Include="IASTNode.cs" />
<Compile Include="Lexer.cs" /> <Compile Include="Lexer.cs" />
<Compile Include="LexingException.cs" /> <Compile Include="LexingException.cs" />
<Compile Include="Parser.cs" />
<Compile Include="ParsingException.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SSType.cs" />
<Compile Include="Token.cs" /> <Compile Include="Token.cs" />
<Compile Include="TypeException.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View File

@ -0,0 +1,91 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ShintenScript;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShintenScriptTest
{
[TestClass]
public class EvaluationTest
{
[TestMethod]
public void TestBlock()
{
IASTNode ast = new ASTNodeBlock(new List<IASTNode>()
{
new ASTNodeLiteral(1f),
new ASTNodeLiteral(true),
new ASTNodeLiteral(5f)
});
Assert.AreEqual(SSType.real, ast.Type());
Func<ExecutionContext, float> func = (Func<ExecutionContext, float>)ast.CreateFunction();
Assert.AreEqual(5f, func(null));
}
[TestMethod]
public void TestAdd()
{
IASTNode ast = new ASTNodeAdd(new ASTNodeLiteral(2f), new ASTNodeLiteral(3f));
Assert.AreEqual(SSType.real, ast.Type());
Func<ExecutionContext, float> func = (Func<ExecutionContext, float>)ast.CreateFunction();
Assert.AreEqual(5f, func(null));
}
[TestMethod]
public void TestMul()
{
IASTNode ast = new ASTNodeMultiply(new ASTNodeLiteral(2f), new ASTNodeLiteral(3f));
Assert.AreEqual(SSType.real, ast.Type());
Func<ExecutionContext, float> func = (Func<ExecutionContext, float>)ast.CreateFunction();
Assert.AreEqual(6f, func(null));
}
[TestMethod]
public void TestSub()
{
IASTNode ast = new ASTNodeSubtract(new ASTNodeLiteral(2f), new ASTNodeLiteral(3f));
Assert.AreEqual(SSType.real, ast.Type());
Func<ExecutionContext, float> func = (Func<ExecutionContext, float>)ast.CreateFunction();
Assert.AreEqual(-1f, func(null));
}
[TestMethod]
public void TestDiv()
{
IASTNode ast = new ASTNodeDivide(new ASTNodeLiteral(7f), new ASTNodeLiteral(2f));
Assert.AreEqual(SSType.real, ast.Type());
Func<ExecutionContext, float> func = (Func<ExecutionContext, float>)ast.CreateFunction();
Assert.AreEqual(3.5f, func(null));
}
[TestMethod]
public void TestNegative()
{
IASTNode ast = new ASTNodeNegative(new ASTNodeLiteral(7f));
Assert.AreEqual(SSType.real, ast.Type());
Func<ExecutionContext, float> func = (Func<ExecutionContext, float>)ast.CreateFunction();
Assert.AreEqual(-7f, func(null));
}
}
}

View File

@ -19,6 +19,7 @@ namespace ShintenScriptTest
1.5e6 1.5e6
2.4e-6 2.4e-6
;
( ) { } ( ) { }
+ - * / + - * /
> < == >= <= != > < == >= <= !=
@ -36,6 +37,7 @@ namespace ShintenScriptTest
new Token { type = Token.Type.NUMBER, data = 1.5e6f }, new Token { type = Token.Type.NUMBER, data = 1.5e6f },
new Token { type = Token.Type.NUMBER, data = 2.4e-6f }, new Token { type = Token.Type.NUMBER, data = 2.4e-6f },
new Token { type = Token.Type.SEMICOLON },
new Token { type = Token.Type.LPAREN }, new Token { type = Token.Type.LPAREN },
new Token { type = Token.Type.RPAREN }, new Token { type = Token.Type.RPAREN },
new Token { type = Token.Type.LBRACE }, new Token { type = Token.Type.LBRACE },
@ -59,8 +61,18 @@ namespace ShintenScriptTest
new Token { type = Token.Type.IF }, new Token { type = Token.Type.IF },
new Token { type = Token.Type.WHILE }, new Token { type = Token.Type.WHILE },
new Token { type = Token.Type.FN }, new Token { type = Token.Type.FN },
}));
}
new Token { type = Token.Type.EOF }, [TestMethod]
public void TestHexExponent()
{
Lexer lexer = new Lexer(@"0xcafe-1337");
Assert.IsTrue(lexer.TokenStream().SequenceEqual(new Token[] {
new Token { type = Token.Type.NUMBER, data = 51966f },
new Token { type = Token.Type.MINUS },
new Token { type = Token.Type.NUMBER, data = 1337f },
})); }));
} }
} }

View File

@ -0,0 +1,153 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ShintenScript;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShintenScriptTest
{
[TestClass]
public class ParserTest
{
[TestMethod]
public void TestBlock()
{
Lexer lexer = new ScaffoldLexer(new Token[]
{
new Token { type = Token.Type.LBRACE },
new Token { type = Token.Type.NUMBER, data = 1f },
new Token { type = Token.Type.NUMBER, data = 2f },
new Token { type = Token.Type.NUMBER, data = 3f },
new Token { type = Token.Type.RBRACE },
});
IASTNode ast = new Parser(lexer).ParseExpression();
if (ast is ASTNodeBlock block)
{
Assert.AreEqual(3, block.statements.Count);
Assert.IsInstanceOfType(block.statements[0], typeof(ASTNodeLiteral));
Assert.IsInstanceOfType(block.statements[1], typeof(ASTNodeLiteral));
Assert.IsInstanceOfType(block.statements[2], typeof(ASTNodeLiteral));
}
else Assert.Fail();
}
[TestMethod]
public void TestComparison()
{
Lexer lexer = new ScaffoldLexer(new Token[]
{
new Token { type = Token.Type.NUMBER, data = 1f },
new Token { type = Token.Type.LE },
new Token { type = Token.Type.NUMBER, data = 2f },
new Token { type = Token.Type.NE },
new Token { type = Token.Type.NUMBER, data = 3f },
});
IASTNode ast = new Parser(lexer).ParseExpression();
if (ast is ASTNodeComparison comp)
{
Assert.AreEqual(3, comp.nodes.Count);
Assert.AreEqual(2, comp.comparisons.Count);
Assert.IsInstanceOfType(comp.nodes[0], typeof(ASTNodeLiteral));
Assert.IsInstanceOfType(comp.nodes[1], typeof(ASTNodeLiteral));
Assert.IsInstanceOfType(comp.nodes[2], typeof(ASTNodeLiteral));
Assert.AreEqual(Token.Type.LE, comp.comparisons[0]);
Assert.AreEqual(Token.Type.NE, comp.comparisons[1]);
}
else Assert.Fail();
}
[TestMethod]
public void TestDivision()
{
Lexer lexer = new ScaffoldLexer(new Token[]
{
new Token { type = Token.Type.NUMBER, data = 1f },
new Token { type = Token.Type.SLASH },
new Token { type = Token.Type.NUMBER, data = 2f },
});
IASTNode ast = new Parser(lexer).ParseExpression();
Assert.IsInstanceOfType(ast, typeof(ASTNodeDivide));
}
[TestMethod]
public void TestMinus()
{
Lexer lexer = new ScaffoldLexer(new Token[]
{
new Token { type = Token.Type.MINUS },
new Token { type = Token.Type.NUMBER, data = 2f },
new Token { type = Token.Type.MINUS },
new Token { type = Token.Type.NUMBER, data = 3f },
});
IASTNode ast = new Parser(lexer).ParseExpression();
if (ast is ASTNodeSubtract sub)
{
Assert.IsInstanceOfType(sub.lhs, typeof(ASTNodeNegative));
Assert.IsInstanceOfType(sub.rhs, typeof(ASTNodeLiteral));
}
else Assert.Fail();
}
[TestMethod]
public void TestOrderOfOperations()
{
Lexer lexer = new ScaffoldLexer(new Token[]
{
new Token { type = Token.Type.NUMBER, data = 1f },
new Token { type = Token.Type.PLUS },
new Token { type = Token.Type.NUMBER, data = 2f },
new Token { type = Token.Type.ASTERISK },
new Token { type = Token.Type.NUMBER, data = 3f },
});
IASTNode ast = new Parser(lexer).ParseExpression();
if (ast is ASTNodeAdd add)
{
Assert.IsInstanceOfType(add.lhs, typeof(ASTNodeLiteral));
if (add.rhs is ASTNodeMultiply mul)
{
Assert.IsInstanceOfType(mul.lhs, typeof(ASTNodeLiteral));
Assert.IsInstanceOfType(mul.rhs, typeof(ASTNodeLiteral));
}
else Assert.Fail();
}
else Assert.Fail();
lexer = new ScaffoldLexer(new Token[]
{
new Token { type = Token.Type.NUMBER, data = 1f },
new Token { type = Token.Type.ASTERISK },
new Token { type = Token.Type.NUMBER, data = 2f },
new Token { type = Token.Type.PLUS },
new Token { type = Token.Type.NUMBER, data = 3f },
});
ast = new Parser(lexer).ParseExpression();
if (ast is ASTNodeAdd add2)
{
Assert.IsInstanceOfType(add2.rhs, typeof(ASTNodeLiteral));
if (add2.lhs is ASTNodeMultiply mul)
{
Assert.IsInstanceOfType(mul.lhs, typeof(ASTNodeLiteral));
Assert.IsInstanceOfType(mul.rhs, typeof(ASTNodeLiteral));
}
else Assert.Fail();
}
else Assert.Fail();
}
}
}

View File

@ -0,0 +1,27 @@
using ShintenScript;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShintenScriptTest
{
class ScaffoldLexer : Lexer
{
Queue<Token> tokenQueue;
public ScaffoldLexer(IEnumerable<Token> queue) : base("")
{
tokenQueue = new Queue<Token>(queue);
}
public override Token ParseOne()
{
if (tokenQueue.Count == 0)
return new Token { type = Token.Type.EOF };
return tokenQueue.Dequeue();
}
}
}

View File

@ -49,8 +49,11 @@
<Reference Include="System.Core" /> <Reference Include="System.Core" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="EvaluationTest.cs" />
<Compile Include="LexerTests.cs" /> <Compile Include="LexerTests.cs" />
<Compile Include="ParserTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ScaffoldLexer.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />

View File

@ -1,6 +1,6 @@
LexerTests LexerTests
Tests in group: 1 Tests in group: 13
Total Duration: 31ms Total Duration: 28ms
Outcomes Outcomes
1 Passed 13 Passed

View File

@ -15,6 +15,7 @@ namespace ShintenScript
IDENTIFIER, IDENTIFIER,
NUMBER, NUMBER,
SEMICOLON,
LPAREN, RPAREN, LBRACE, RBRACE, LPAREN, RPAREN, LBRACE, RBRACE,
PLUS, MINUS, ASTERISK, SLASH, PLUS, MINUS, ASTERISK, SLASH,
GT, LT, EQ, GE, LE, NE, GT, LT, EQ, GE, LE, NE,

16
TypeException.cs Normal file
View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShintenScript
{
public class TypeException : Exception
{
public TypeException(string message) : base(message)
{
}
}
}