Parser stuff
This commit is contained in:
parent
7acaa9df76
commit
abbd4a74ef
|
@ -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() => "+";
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() => "/";
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() => "*";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() => "-";
|
||||
}
|
||||
}
|
|
@ -390,19 +390,19 @@ a.percentagebar {
|
|||
<col />
|
||||
</colgroup>
|
||||
<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>Assemblies:</th><td>2</td></tr>
|
||||
<tr><th>Classes:</th><td>4</td></tr>
|
||||
<tr><th>Files:</th><td>4</td></tr>
|
||||
<tr><th>Covered lines:</th><td>165</td></tr>
|
||||
<tr><th>Uncovered lines:</th><td>50</td></tr>
|
||||
<tr><th>Coverable lines:</th><td>215</td></tr>
|
||||
<tr><th>Total lines:</th><td>355</td></tr>
|
||||
<tr><th>Line coverage:</th><td>76.7% (165 of 215)</td></tr>
|
||||
<tr><th>Covered branches:</th><td>44</td></tr>
|
||||
<tr><th>Total branches:</th><td>68</td></tr>
|
||||
<tr><th>Branch coverage:</th><td>64.7% (44 of 68)</td></tr>
|
||||
<tr><th>Classes:</th><td>20</td></tr>
|
||||
<tr><th>Files:</th><td>20</td></tr>
|
||||
<tr><th>Covered lines:</th><td>532</td></tr>
|
||||
<tr><th>Uncovered lines:</th><td>188</td></tr>
|
||||
<tr><th>Coverable lines:</th><td>720</td></tr>
|
||||
<tr><th>Total lines:</th><td>1289</td></tr>
|
||||
<tr><th>Line coverage:</th><td>73.8% (532 of 720)</td></tr>
|
||||
<tr><th>Covered branches:</th><td>112</td></tr>
|
||||
<tr><th>Total branches:</th><td>232</td></tr>
|
||||
<tr><th>Branch coverage:</th><td>48.2% (112 of 232)</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h1>Risk Hotspots</h1>
|
||||
|
@ -427,16 +427,32 @@ a.percentagebar {
|
|||
</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>
|
||||
<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"> </td><td class="red covered31"> </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"> </td><td class="red covered35"> </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"> </td><td class="red covered28"> </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"> </td><td class="red covered30"> </td></tr></table></td></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"> </td><td class="red covered36"> </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"> </td><td class="red covered52"> </td></tr></table></th></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"> </td><td class="red covered20"> </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"> </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"> </td><td class="red covered20"> </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"> </td><td class="red covered60"> </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"> </td><td class="red covered48"> </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"> </td><td class="red covered58"> </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"> </td><td class="red covered95"> </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"> </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"> </td><td class="red covered20"> </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"> </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"> </td><td class="red covered14"> </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"> </td><td class="red covered50"> </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"> </td><td class="red covered20"> </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"> </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"> </td><td class="red covered25"> </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"> </td><td class="red covered50"> </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"> </td><td class="red covered20"> </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"> </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"> </td><td class="red covered13"> </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"> </td><td class="red covered18"> </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"> </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"> </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"> </td><td class="red covered47"> </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"> </td><td class="red covered50"> </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"> </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"> </td></tr></table></th></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"> </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"> </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"> </td><td class="red covered15"> </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"> </td><td class="red covered18"> </td></tr></table></td></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"> </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"> </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"> </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"> </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"> </td><td class="red covered47"> </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"> </td><td class="red covered50"> </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"> </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"> </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"> </td><td class="red covered3"> </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"> </td><td class="red covered44"> </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"> </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"> </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"> </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"> </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"> </td><td class="red covered6"> </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"> </td><td class="red covered50"> </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"> </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"> </td></tr></table></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</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
|
||||
* Copyright © 2017 Gion Kunz
|
||||
* Free to use under either the WTFPL license or the MIT license.
|
||||
|
@ -663,14 +679,30 @@ var assemblies = [
|
|||
{
|
||||
"name": "ShintenScript",
|
||||
"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.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",
|
||||
"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": [] },
|
||||
]},
|
||||
];
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
27
Lexer.cs
27
Lexer.cs
|
@ -20,6 +20,7 @@ namespace ShintenScript
|
|||
("*=", Token.Type.ASTERISKASSIGN),
|
||||
("/=", Token.Type.SLASHASSIGN),
|
||||
|
||||
(";", Token.Type.SEMICOLON),
|
||||
("+", Token.Type.PLUS),
|
||||
("-", Token.Type.MINUS),
|
||||
("*", Token.Type.ASTERISK),
|
||||
|
@ -51,7 +52,7 @@ namespace ShintenScript
|
|||
|
||||
Token current = new Token { type = Token.Type.NULL };
|
||||
|
||||
Token ParseOne()
|
||||
public virtual Token ParseOne()
|
||||
{
|
||||
if (EOF())
|
||||
return new Token { type = Token.Type.EOF };
|
||||
|
@ -77,7 +78,7 @@ namespace ShintenScript
|
|||
{
|
||||
StringBuilder identifierBuilder = new StringBuilder();
|
||||
|
||||
while (char.IsLetterOrDigit(code[index]))
|
||||
while (!EOF() && char.IsLetterOrDigit(code[index]))
|
||||
{
|
||||
identifierBuilder.Append(code[index]);
|
||||
index++;
|
||||
|
@ -97,10 +98,15 @@ namespace ShintenScript
|
|||
if (char.IsNumber(code[index]))
|
||||
{
|
||||
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]);
|
||||
|
||||
if (char.IsLetter(code[index]) && code[index] != 'e' && code[index] != 'E')
|
||||
seenLetter = true;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
@ -187,24 +193,29 @@ namespace ShintenScript
|
|||
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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()
|
||||
{
|
||||
while (!EOF())
|
||||
while (true)
|
||||
{
|
||||
yield return Pop();
|
||||
Token token = Pop();
|
||||
|
||||
if (token.type == Token.Type.EOF)
|
||||
break;
|
||||
|
||||
yield return token;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -41,10 +41,25 @@
|
|||
<Reference Include="System.Xml" />
|
||||
</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="LexingException.cs" />
|
||||
<Compile Include="Parser.cs" />
|
||||
<Compile Include="ParsingException.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SSType.cs" />
|
||||
<Compile Include="Token.cs" />
|
||||
<Compile Include="TypeException.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ namespace ShintenScriptTest
|
|||
1.5e6
|
||||
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 = 2.4e-6f },
|
||||
|
||||
new Token { type = Token.Type.SEMICOLON },
|
||||
new Token { type = Token.Type.LPAREN },
|
||||
new Token { type = Token.Type.RPAREN },
|
||||
new Token { type = Token.Type.LBRACE },
|
||||
|
@ -59,8 +61,18 @@ namespace ShintenScriptTest
|
|||
new Token { type = Token.Type.IF },
|
||||
new Token { type = Token.Type.WHILE },
|
||||
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 },
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,8 +49,11 @@
|
|||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="EvaluationTest.cs" />
|
||||
<Compile Include="LexerTests.cs" />
|
||||
<Compile Include="ParserTest.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ScaffoldLexer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
LexerTests
|
||||
Tests in group: 1
|
||||
Total Duration: 31ms
|
||||
Tests in group: 13
|
||||
Total Duration: 28ms
|
||||
|
||||
Outcomes
|
||||
1 Passed
|
||||
13 Passed
|
1
Token.cs
1
Token.cs
|
@ -15,6 +15,7 @@ namespace ShintenScript
|
|||
IDENTIFIER,
|
||||
NUMBER,
|
||||
|
||||
SEMICOLON,
|
||||
LPAREN, RPAREN, LBRACE, RBRACE,
|
||||
PLUS, MINUS, ASTERISK, SLASH,
|
||||
GT, LT, EQ, GE, LE, NE,
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue