Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
62dfd01
Add InterpolatedStringHandler For OutputManagement
furesoft Nov 5, 2024
b60cd8e
Cleanup
furesoft Nov 5, 2024
4d82d7c
Use pointers instead of references for storing match outputs
furesoft Nov 5, 2024
924f8b3
Add records to represent a parsed pattern
furesoft Nov 5, 2024
2b228d1
Add matching binary operator
furesoft Nov 5, 2024
5d358f5
Refactoring
furesoft Nov 5, 2024
52dc451
Cleanup
furesoft Nov 6, 2024
f0f297c
Add pattern parsing and barebones of matching
furesoft Nov 10, 2024
f624f90
Cleanup
furesoft Nov 10, 2024
24201d2
Add String Matching
furesoft Nov 11, 2024
a662801
Add Not Operator
furesoft Nov 11, 2024
6293639
Add TypeSpecifier Parsing
furesoft Nov 11, 2024
d94a005
Add Type Matching
furesoft Nov 11, 2024
f41f134
Use _ instead of ? to ignore an argument
furesoft Nov 11, 2024
82884f7
Cleanup
furesoft Nov 11, 2024
03eae64
Testing Stuff
furesoft Nov 11, 2024
6ecbd15
Add stub method
furesoft Nov 11, 2024
2dd666a
Add Number Operator Parsing
furesoft Nov 12, 2024
b10725b
Merge branch 'feature/matching-dsl' of https://github.com/furesoft/Di…
furesoft Nov 12, 2024
3338734
Parsing Optimizations
furesoft Nov 12, 2024
ff58561
Implement WIP Number Operator Matching
furesoft Nov 12, 2024
0862af8
Cleanup
furesoft Nov 12, 2024
032e712
Replace dangerous code
furesoft Nov 12, 2024
4a28439
Cleanup
furesoft Nov 12, 2024
f187eac
Add simple doc entry
furesoft Nov 13, 2024
f24bf15
Merge branch 'feature/matching-dsl' of https://github.com/furesoft/Di…
furesoft Nov 13, 2024
9e6aaa8
Cleanup
furesoft Nov 13, 2024
b862c1b
Cleanup
furesoft Nov 14, 2024
3572b17
Add Buffered Argument
furesoft Nov 16, 2024
0a56ad4
Add Output SubPatterns with matching
furesoft Nov 16, 2024
b0e1d05
Fix number operator
furesoft Nov 16, 2024
dde872f
Optimizations
furesoft Nov 16, 2024
b5fc68d
Cleanup
furesoft Nov 16, 2024
4a21df8
Add Compare Instruction Matching
furesoft Nov 16, 2024
bc33aee
Add More Instruction Matching
furesoft Nov 16, 2024
de5cd41
Add Docs for the dsl pattern
furesoft Nov 16, 2024
825cdd8
Merge branch 'main' into feature/matching-dsl
furesoft Nov 16, 2024
e2099c2
Add simple replacement
furesoft Nov 25, 2024
bf26d76
feat: Add Call Matching Stuff
furesoft Nov 27, 2024
5551736
Add binary and compare instruction creation
furesoft Nov 30, 2024
d2a32da
Merge branch 'feature/matching-dsl' of https://github.com/furesoft/Di…
dubiousconst282 Dec 4, 2024
ea20868
Add early return to MatchOperands
furesoft Dec 7, 2024
12fb038
Add todo
furesoft Dec 7, 2024
ef11dd4
Move stub to TestAsm
furesoft Dec 7, 2024
78cd2d3
Merge branch 'feature/matching-dsl' of https://github.com/furesoft/Di…
dubiousconst282 Dec 7, 2024
9f12b70
Fix test crashing
dubiousconst282 Dec 8, 2024
000059d
Merge branch 'main' into furesoft-feature/matching-dsl
dubiousconst282 Dec 27, 2024
941e00f
Fix const matching
dubiousconst282 Dec 27, 2024
b644122
Add opcode is/get concrete op extensions
dubiousconst282 Dec 27, 2024
c428fe0
fix unit tests
furesoft Jan 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/api-walkthrough.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,31 @@ if (inst is BinaryInst { Op: BinaryOp.Sub, Left: BinaryInst { Op: BinaryOp.Add }
}
```

or you can use the pattern matching dsl:
```csharp
if (ins.Match("(sub {lhs: (add {x} $y)} $y)", out var outputs))
{
inst.ReplaceWith(outputs["lhs"]);
}
```

Pattern Matching Operations:
| Example | Description |
|------------|------------------------------------------------------------------------|
| $x | Use the value later for matching |
| {x} | Output the operand |
| {x: (add)} | Output the operand if the subpattern matched |
| _ | Ignore the operand |
| ! | Matches if the subpattern doesn't match |
| #instr | Matches if the operand is an instruction |
| #const | Matches if the operand is a constant |
| #int | Matches if the operand is a constant of type int32 |
| >5 | Matches if the value is greater than 5 |
| <5 | Matches if the value is less than 5 |
| *"hello"* | Matches if the operand is a string constant that contains the value |
| *"hello" | Matches if the operand is a string constant that end with the value |
| "hello"* | Matches if the operand is a string constant that starts with the value |
=======
## Finding Methods

The module resolver has an `FindMethod` extension method that makes it easier to find methods from the IR.
Expand Down
2 changes: 1 addition & 1 deletion src/DistIL/AsmIO/ResolvingUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public static class ResolvingUtils
return methods.FirstOrDefault();
}

private static MethodSelector GetSelector(this ModuleResolver resolver, string selector)
internal static MethodSelector GetSelector(this ModuleResolver resolver, string selector)
{
var spl = selector.Split("::");
var type = FindType(resolver, spl[0].Trim());
Expand Down
6 changes: 6 additions & 0 deletions src/DistIL/IR/DSL/IInstructionPatternArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DistIL.IR.DSL;

internal interface IInstructionPatternArgument
{

}
227 changes: 227 additions & 0 deletions src/DistIL/IR/DSL/InstructionPattern.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
namespace DistIL.IR.DSL;

using System;
using System.Collections.Generic;

using PatternArguments;
using Utils.Parser;

internal record InstructionPattern(
Opcode OpCode,
string Operation,
List<IInstructionPatternArgument> Arguments)
: IInstructionPatternArgument
{
public static IInstructionPatternArgument? Parse(ReadOnlySpan<char> pattern)
{
// Remove whitespace and validate parentheses balance
pattern = pattern.Trim();
if (pattern.Length == 0) {
return null;
}

if (pattern[0] != '(' || pattern[^1] != ')')
return ParseArgument(pattern);

// Remove the outer parentheses
pattern = pattern[1..^1].Trim();

// Split the operation from its arguments
int spaceIndex = pattern.IndexOf(' ');
if (spaceIndex == -1) {
spaceIndex = pattern.Length;
}

var op = pattern[..spaceIndex].ToString();
var operation = Opcodes.TryParse(op); // TryParse does not support span yet
var argsString = pattern[spaceIndex..].Trim();

List<IInstructionPatternArgument> arguments = new List<IInstructionPatternArgument>();

if (operation.Op is Opcode.Call or Opcode.CallVirt) {
var selector = argsString[..argsString.IndexOf(' ')].ToString();
arguments.Add(new MethodRefArgument(selector));

argsString = argsString[argsString.IndexOf(' ')..];
}

ParseArguments(argsString, arguments);

return new InstructionPattern(operation.Op, op, arguments);
}

private static IInstructionPatternArgument? ParseEval(ReadOnlySpan<char> pattern)
{
var op = pattern[1..].Trim();

return new EvalArgument(ParseArgument(op));
}

private static void ParseArguments(ReadOnlySpan<char> argsString, List<IInstructionPatternArgument> arguments)
{
int depth = 0;
string currentArg = "";
Stack<char> outputStack = new();

foreach (var c in argsString)
{
if (c == '{') {
outputStack.Push(c);
}
else if (c == '}') {
outputStack.Pop();
}

if (c == '(')
{
depth++;
currentArg += c;
}
else if (c == ')')
{
depth--;
currentArg += c;
if (depth == 0 && outputStack.Count == 0)
{
// Completed a nested argument
arguments.Add(Parse(currentArg.AsSpan())!);
currentArg = "";
}
}
else if (char.IsWhiteSpace(c) && depth == 0)
{
// End of a top-level argument
if (!string.IsNullOrWhiteSpace(currentArg))
{
arguments.Add(ParseArgument(currentArg.Trim()));
currentArg = "";
}
}
else
{
currentArg += c;
}
}

// Add any remaining argument
if (!string.IsNullOrWhiteSpace(currentArg))
{
arguments.Add(ParseArgument(currentArg.Trim()));
}
}

private static IInstructionPatternArgument ParseArgument(ReadOnlySpan<char> arg)
{
if (arg[0] == '(' && arg[^1] == ')') {
return Parse(arg)!;
}

if (arg.Contains('#'))
{
var left = arg[..arg.IndexOf('#')];
var typeSpecifier = arg[arg.IndexOf('#')..].TrimStart('#');

var argument = left is not "" ? ParseArgument(left) : null;
return new TypedArgument(argument, typeSpecifier.ToString());
}

if (arg[0] == '!') {
return ParseNot(arg);
}

if (arg[0] == '$') {
return ParseBuffer(arg);
}

if (arg[0] == '<' || arg[0] == '>') {
return ParseNumOperator(arg);
}

if (arg[0] == '#') {
return new TypedArgument(default, arg[1..].ToString());
}

if (arg[0] == '*' || arg[0] == '\'')
{
return ParseStringArgument(arg);
}

if (arg[0] == '{' && arg[^1] == '}') {
return ParseOutputArgument(arg);
}

if (arg[0] == '_')
{
return new IgnoreArgument();
}

if (long.TryParse(arg, out var number))
{
return new ConstantArgument(number, PrimType.Int64);
}
if (double.TryParse(arg, out var dnumber))
{
return new ConstantArgument(dnumber, PrimType.Double);
}

throw new ArgumentException("Invalid Argument");
}

private static IInstructionPatternArgument ParseOutputArgument(ReadOnlySpan<char> arg)
{
arg = arg[1..^1];

if (arg.Contains(':')) {
var name = arg[..arg.IndexOf(':')];
var subPattern = ParseArgument(arg[(arg.IndexOf(':') + 1)..]);

return new OutputArgument(name.ToString(), subPattern);
}

return new OutputArgument(arg.ToString());
}

private static IInstructionPatternArgument ParseBuffer(ReadOnlySpan<char> arg)
{
return new BufferArgument(arg[1..].ToString());
}

private static IInstructionPatternArgument ParseNumOperator(ReadOnlySpan<char> arg)
{
var op = arg[0];

return new NumberOperatorArgument(op, ParseArgument(arg[1..]));
}


private static IInstructionPatternArgument ParseNot(ReadOnlySpan<char> arg)
{
var trimmed = arg.TrimStart('!');

return new NotArgument(ParseArgument(trimmed));
}

private static IInstructionPatternArgument ParseStringArgument(ReadOnlySpan<char> arg)
{
StringOperation operation = StringOperation.None;

if (arg[0] == '*' && arg[^1] == '*') {
operation = StringOperation.Contains;
}
else if(arg[0] == '*') {
operation = StringOperation.EndsWith;
}
else if(arg[^1] == '*') {
operation = StringOperation.StartsWith;
}

arg = arg.TrimStart('*').TrimEnd('*');

if (arg[0] == '\'' && arg[^1] == '\'') {
return new StringArgument(arg[1..^1].ToString(), operation);
}

throw new ArgumentException("Invalid string");
}

}
48 changes: 48 additions & 0 deletions src/DistIL/IR/DSL/OutputPattern.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
namespace DistIL.IR.DSL;

public readonly struct OutputPattern
{
private readonly Dictionary<string, Value> _outputs = [];
private readonly Dictionary<string, Value> _buffer = [];
internal readonly InstructionPattern? Pattern = null;

public OutputPattern(ReadOnlySpan<char> input)
{
Pattern = InstructionPattern.Parse(input) as InstructionPattern;
}

internal void Add(string key, Value value)
{
_outputs[key] = value;
}

internal void AddToBuffer(string key, Value value)
{
_buffer[key] = value;
}

public Value this[string name] => _outputs[name];

public Value this[int position] {
get {
string name = _outputs.Keys.ElementAt(position);

return _outputs[name];
}
}

internal Value GetFromBuffer(string name)
{
return _buffer[name];
}

internal Value? Get(string name)
{
return _buffer.TryGetValue(name, out Value? value) ? value : _outputs.GetValueOrDefault(name);
}

internal bool IsValueInBuffer(string name)
{
return _buffer.ContainsKey(name);
}
}
3 changes: 3 additions & 0 deletions src/DistIL/IR/DSL/PatternArguments/BufferArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace DistIL.IR.DSL.PatternArguments;

internal record BufferArgument(string Name) : IInstructionPatternArgument;
3 changes: 3 additions & 0 deletions src/DistIL/IR/DSL/PatternArguments/EvalArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace DistIL.IR.DSL.PatternArguments;

internal record EvalArgument(IInstructionPatternArgument OP) : IInstructionPatternArgument;
6 changes: 6 additions & 0 deletions src/DistIL/IR/DSL/PatternArguments/IgnoreArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DistIL.IR.DSL.PatternArguments;

internal record IgnoreArgument : IInstructionPatternArgument
{

}
3 changes: 3 additions & 0 deletions src/DistIL/IR/DSL/PatternArguments/MethodRefArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace DistIL.IR.DSL.PatternArguments;

internal record MethodRefArgument(string Selector) : IInstructionPatternArgument;
6 changes: 6 additions & 0 deletions src/DistIL/IR/DSL/PatternArguments/NotArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DistIL.IR.DSL.PatternArguments;

internal record NotArgument(IInstructionPatternArgument Inner) : IInstructionPatternArgument
{

}
6 changes: 6 additions & 0 deletions src/DistIL/IR/DSL/PatternArguments/NumberArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DistIL.IR.DSL.PatternArguments;

internal record ConstantArgument(object Value, TypeDesc? Type) : IInstructionPatternArgument
{

}
6 changes: 6 additions & 0 deletions src/DistIL/IR/DSL/PatternArguments/NumberOperatorArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DistIL.IR.DSL.PatternArguments;

internal record NumberOperatorArgument(char Operator, IInstructionPatternArgument Argument) : IInstructionPatternArgument
{

}
6 changes: 6 additions & 0 deletions src/DistIL/IR/DSL/PatternArguments/OutputArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DistIL.IR.DSL.PatternArguments;

internal record OutputArgument(string Name, IInstructionPatternArgument? SubPattern = null) : IInstructionPatternArgument
{

}
6 changes: 6 additions & 0 deletions src/DistIL/IR/DSL/PatternArguments/StringArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DistIL.IR.DSL.PatternArguments;

internal record StringArgument(object Value, StringOperation Operation) : ConstantArgument(Value, PrimType.String)
{

}
9 changes: 9 additions & 0 deletions src/DistIL/IR/DSL/PatternArguments/StringOperation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace DistIL.IR.DSL.PatternArguments;

internal enum StringOperation
{
None,
StartsWith,
EndsWith,
Contains
}
Loading