Bit manipulation
This commit is contained in:
@@ -4,11 +4,19 @@ extern func puts(text: cstring)
|
||||
func main(args: []cstring): i64
|
||||
{
|
||||
let x: u32 = 23
|
||||
test(x)
|
||||
|
||||
if x == 23 && true
|
||||
{
|
||||
puts("yes")
|
||||
}
|
||||
else
|
||||
{
|
||||
puts("no")
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func test(test: u32)
|
||||
{
|
||||
puts("test")
|
||||
}
|
||||
@@ -725,102 +725,162 @@ public class QBEGenerator
|
||||
|
||||
var outputName = TmpName();
|
||||
|
||||
var instruction = EmitBinaryInstructionFor(binaryExpression.Operator, binaryExpression.Left.Type, left, right);
|
||||
var instruction = BinaryInstructionFor(binaryExpression.Operator, binaryExpression.Left.Type);
|
||||
|
||||
_writer.Indented($"{outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}");
|
||||
return outputName;
|
||||
}
|
||||
|
||||
private string EmitBinaryInstructionFor(BinaryOperator op, TypeNode type, string left, string right)
|
||||
private string BinaryInstructionFor(BinaryOperator op, TypeNode type)
|
||||
{
|
||||
if (op is
|
||||
BinaryOperator.Equal or
|
||||
BinaryOperator.NotEqual or
|
||||
BinaryOperator.GreaterThan or
|
||||
BinaryOperator.GreaterThanOrEqual or
|
||||
BinaryOperator.LessThan or
|
||||
BinaryOperator.LessThanOrEqual)
|
||||
{
|
||||
char suffix;
|
||||
|
||||
if (!type.IsSimpleType(out var simpleType, out _))
|
||||
{
|
||||
throw new NotSupportedException("Binary operations is only supported for simple types.");
|
||||
}
|
||||
|
||||
switch (simpleType.StorageSize)
|
||||
{
|
||||
case StorageSize.I8:
|
||||
_writer.Indented($"{left} =w extsb {left}");
|
||||
_writer.Indented($"{right} =w extsb {right}");
|
||||
suffix = 'w';
|
||||
break;
|
||||
case StorageSize.U8:
|
||||
_writer.Indented($"{left} =w extub {left}");
|
||||
_writer.Indented($"{right} =w extub {right}");
|
||||
suffix = 'w';
|
||||
break;
|
||||
case StorageSize.I16:
|
||||
_writer.Indented($"{left} =w extsh {left}");
|
||||
_writer.Indented($"{right} =w extsh {right}");
|
||||
suffix = 'w';
|
||||
break;
|
||||
case StorageSize.U16:
|
||||
_writer.Indented($"{left} =w extuh {left}");
|
||||
_writer.Indented($"{right} =w extuh {right}");
|
||||
suffix = 'w';
|
||||
break;
|
||||
case StorageSize.I32 or StorageSize.U32:
|
||||
suffix = 'w';
|
||||
break;
|
||||
case StorageSize.I64 or StorageSize.U64:
|
||||
suffix = 'l';
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"Unsupported type '{simpleType}' for binary operator '{op}'");
|
||||
}
|
||||
|
||||
if (op is BinaryOperator.Equal)
|
||||
{
|
||||
return "ceq" + suffix;
|
||||
}
|
||||
|
||||
if (op is BinaryOperator.NotEqual)
|
||||
{
|
||||
return "cne" + suffix;
|
||||
}
|
||||
|
||||
string sign;
|
||||
|
||||
if (simpleType is IntTypeNode { Signed: true })
|
||||
{
|
||||
sign = "s";
|
||||
}
|
||||
else if (simpleType is IntTypeNode { Signed: false })
|
||||
{
|
||||
sign = "u";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Unsupported type '{type}' for binary operator '{op}'");
|
||||
}
|
||||
|
||||
return op switch
|
||||
{
|
||||
BinaryOperator.GreaterThan => 'c' + sign + "gt" + suffix,
|
||||
BinaryOperator.GreaterThanOrEqual => 'c' + sign + "ge" + suffix,
|
||||
BinaryOperator.LessThan => 'c' + sign + "lt" + suffix,
|
||||
BinaryOperator.LessThanOrEqual => 'c' + sign + "le" + suffix,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(op), op, null)
|
||||
};
|
||||
}
|
||||
|
||||
return op switch
|
||||
BinaryOperator.RightShift => type switch
|
||||
{
|
||||
IntTypeNode { Signed: true } => "sar",
|
||||
IntTypeNode { Signed: false } => "shr",
|
||||
_ => throw new NotSupportedException($"Right shift not supported for type '{type}'")
|
||||
},
|
||||
BinaryOperator.BitwiseAnd => "and",
|
||||
BinaryOperator.BitwiseOr => "or",
|
||||
BinaryOperator.BitwiseXor => "xor",
|
||||
BinaryOperator.LeftShift => "shl",
|
||||
BinaryOperator.Divide => type switch
|
||||
{
|
||||
IntTypeNode { Signed: true } => "div",
|
||||
IntTypeNode { Signed: false } => "udiv",
|
||||
FloatTypeNode => "div",
|
||||
_ => throw new NotSupportedException($"Division not supported for type '{type}'")
|
||||
},
|
||||
BinaryOperator.Modulo => type switch
|
||||
{
|
||||
IntTypeNode { Signed: true } => "rem",
|
||||
IntTypeNode { Signed: false } => "urem",
|
||||
_ => throw new NotSupportedException($"Modulo not supported for type '{type}'")
|
||||
},
|
||||
BinaryOperator.Plus => "add",
|
||||
BinaryOperator.Minus => "sub",
|
||||
BinaryOperator.Multiply => "mul",
|
||||
BinaryOperator.Divide => "div",
|
||||
BinaryOperator.Equal => type switch
|
||||
{
|
||||
IntTypeNode intType => intType.Width switch
|
||||
{
|
||||
<= 32 => "ceqw",
|
||||
64 => "ceql",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
FloatTypeNode floatType => floatType.Width switch
|
||||
{
|
||||
32 => "ceqs",
|
||||
64 => "ceqd",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
_ => throw new NotSupportedException($"Equality comparison not supported for type '{type}'")
|
||||
},
|
||||
BinaryOperator.NotEqual => type switch
|
||||
{
|
||||
IntTypeNode intType => intType.Width switch
|
||||
{
|
||||
<= 32 => "cnew",
|
||||
64 => "cnel",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
FloatTypeNode floatType => floatType.Width switch
|
||||
{
|
||||
32 => "cnes",
|
||||
64 => "cned",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
_ => throw new NotSupportedException($"Inequality comparison not supported for type '{type}'")
|
||||
},
|
||||
BinaryOperator.LessThan => type switch
|
||||
{
|
||||
IntTypeNode { Signed: true } intType => intType.Width switch
|
||||
{
|
||||
<= 32 => "csltw",
|
||||
64 => "csltl",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
IntTypeNode { Signed: false } intType => intType.Width switch
|
||||
{
|
||||
<= 32 => "cultw",
|
||||
64 => "cultl",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
FloatTypeNode floatType => floatType.Width switch
|
||||
{
|
||||
32 => "clts",
|
||||
64 => "cltd",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
_ => throw new NotSupportedException($"Less than comparison not supported for type '{type}'")
|
||||
},
|
||||
BinaryOperator.LessThanOrEqual => type switch
|
||||
{
|
||||
IntTypeNode { Signed: true } intType => intType.Width switch
|
||||
{
|
||||
<= 32 => "cslew",
|
||||
64 => "cslel",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
IntTypeNode { Signed: false } intType => intType.Width switch
|
||||
{
|
||||
<= 32 => "culew",
|
||||
64 => "culel",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
FloatTypeNode floatType => floatType.Width switch
|
||||
{
|
||||
32 => "cles",
|
||||
64 => "cled",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
_ => throw new NotSupportedException($"Less than or equal comparison not supported for type '{type}'")
|
||||
},
|
||||
BinaryOperator.GreaterThan => type switch
|
||||
{
|
||||
IntTypeNode { Signed: true } intType => intType.Width switch
|
||||
{
|
||||
<= 32 => "csgtw",
|
||||
64 => "csgtl",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
IntTypeNode { Signed: false } intType => intType.Width switch
|
||||
{
|
||||
<= 32 => "cugtw",
|
||||
64 => "cugtl",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
FloatTypeNode floatType => floatType.Width switch
|
||||
{
|
||||
32 => "cgts",
|
||||
64 => "cgtd",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
_ => throw new NotSupportedException($"Greater than comparison not supported for type '{type}'")
|
||||
},
|
||||
BinaryOperator.GreaterThanOrEqual => type switch
|
||||
{
|
||||
IntTypeNode { Signed: true } intType => intType.Width switch
|
||||
{
|
||||
<= 32 => "csgew",
|
||||
64 => "csgel",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
IntTypeNode { Signed: false } intType => intType.Width switch
|
||||
{
|
||||
<= 32 => "cugew",
|
||||
64 => "cugel",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
FloatTypeNode floatType => floatType.Width switch
|
||||
{
|
||||
32 => "cges",
|
||||
64 => "cged",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
_ => throw new NotSupportedException($"Greater than or equal comparison not supported for type '{type}'")
|
||||
},
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(op))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -348,16 +348,28 @@ public sealed class Parser
|
||||
{
|
||||
return operatorSyntax switch
|
||||
{
|
||||
BinaryOperatorSyntax.Multiply => 3,
|
||||
BinaryOperatorSyntax.Divide => 3,
|
||||
BinaryOperatorSyntax.Plus => 2,
|
||||
BinaryOperatorSyntax.Minus => 2,
|
||||
BinaryOperatorSyntax.GreaterThan => 1,
|
||||
BinaryOperatorSyntax.GreaterThanOrEqual => 1,
|
||||
BinaryOperatorSyntax.LessThan => 1,
|
||||
BinaryOperatorSyntax.LessThanOrEqual => 1,
|
||||
BinaryOperatorSyntax.Equal => 0,
|
||||
BinaryOperatorSyntax.NotEqual => 0,
|
||||
BinaryOperatorSyntax.Multiply => 6,
|
||||
BinaryOperatorSyntax.Divide => 6,
|
||||
BinaryOperatorSyntax.Modulo => 6,
|
||||
|
||||
BinaryOperatorSyntax.Plus => 5,
|
||||
BinaryOperatorSyntax.Minus => 5,
|
||||
|
||||
BinaryOperatorSyntax.LeftShift => 4,
|
||||
BinaryOperatorSyntax.RightShift => 4,
|
||||
|
||||
BinaryOperatorSyntax.GreaterThan => 3,
|
||||
BinaryOperatorSyntax.GreaterThanOrEqual => 3,
|
||||
BinaryOperatorSyntax.LessThan => 3,
|
||||
BinaryOperatorSyntax.LessThanOrEqual => 3,
|
||||
|
||||
BinaryOperatorSyntax.Equal => 2,
|
||||
BinaryOperatorSyntax.NotEqual => 2,
|
||||
|
||||
BinaryOperatorSyntax.BitwiseAnd => 1,
|
||||
BinaryOperatorSyntax.BitwiseXor => 0,
|
||||
BinaryOperatorSyntax.BitwiseOr => -1,
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(operatorSyntax), operatorSyntax, null)
|
||||
};
|
||||
}
|
||||
@@ -396,6 +408,24 @@ public sealed class Parser
|
||||
case Symbol.ForwardSlash:
|
||||
binaryExpressionOperator = BinaryOperatorSyntax.Divide;
|
||||
return true;
|
||||
case Symbol.Percent:
|
||||
binaryExpressionOperator = BinaryOperatorSyntax.Modulo;
|
||||
return true;
|
||||
case Symbol.LeftShift:
|
||||
binaryExpressionOperator = BinaryOperatorSyntax.LeftShift;
|
||||
return true;
|
||||
case Symbol.RightShift:
|
||||
binaryExpressionOperator = BinaryOperatorSyntax.RightShift;
|
||||
return true;
|
||||
case Symbol.Ampersand:
|
||||
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseAnd;
|
||||
return true;
|
||||
case Symbol.Pipe:
|
||||
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr;
|
||||
return true;
|
||||
case Symbol.Caret:
|
||||
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor;
|
||||
return true;
|
||||
default:
|
||||
binaryExpressionOperator = null;
|
||||
return false;
|
||||
|
||||
@@ -19,14 +19,20 @@ public enum BinaryOperatorSyntax
|
||||
Plus,
|
||||
Minus,
|
||||
Multiply,
|
||||
Divide
|
||||
Divide,
|
||||
Modulo,
|
||||
LeftShift,
|
||||
RightShift,
|
||||
BitwiseAnd,
|
||||
BitwiseXor,
|
||||
BitwiseOr
|
||||
}
|
||||
|
||||
public abstract record ExpressionSyntax(IEnumerable<Token> Tokens) : SyntaxNode(Tokens);
|
||||
|
||||
public record BinaryExpressionSyntax(IEnumerable<Token> Tokens, ExpressionSyntax Left, BinaryOperatorSyntax OperatorSyntax, ExpressionSyntax Right) : ExpressionSyntax(Tokens);
|
||||
public record BinaryExpressionSyntax(IEnumerable<Token> Tokens, ExpressionSyntax Left, BinaryOperatorSyntax Operator, ExpressionSyntax Right) : ExpressionSyntax(Tokens);
|
||||
|
||||
public record UnaryExpressionSyntax(IEnumerable<Token> Tokens, UnaryOperatorSyntax OperatorSyntax, ExpressionSyntax Operand) : ExpressionSyntax(Tokens);
|
||||
public record UnaryExpressionSyntax(IEnumerable<Token> Tokens, UnaryOperatorSyntax Operator, ExpressionSyntax Operand) : ExpressionSyntax(Tokens);
|
||||
|
||||
public record FuncCallSyntax(IEnumerable<Token> Tokens, ExpressionSyntax Expression, IReadOnlyList<ExpressionSyntax> Parameters) : ExpressionSyntax(Tokens);
|
||||
|
||||
|
||||
@@ -70,4 +70,8 @@ public enum Symbol
|
||||
For,
|
||||
Extern,
|
||||
Semi,
|
||||
Percent,
|
||||
LeftShift,
|
||||
RightShift,
|
||||
Pipe,
|
||||
}
|
||||
@@ -28,6 +28,8 @@ public sealed class Tokenizer
|
||||
[['!', '=']] = Symbol.NotEqual,
|
||||
[['<', '=']] = Symbol.LessThanOrEqual,
|
||||
[['>', '=']] = Symbol.GreaterThanOrEqual,
|
||||
[['<', '<']] = Symbol.LeftShift,
|
||||
[['>', '>']] = Symbol.RightShift,
|
||||
[[':']] = Symbol.Colon,
|
||||
[['(']] = Symbol.OpenParen,
|
||||
[[')']] = Symbol.CloseParen,
|
||||
@@ -48,6 +50,8 @@ public sealed class Tokenizer
|
||||
[['^']] = Symbol.Caret,
|
||||
[['&']] = Symbol.Ampersand,
|
||||
[[';']] = Symbol.Semi,
|
||||
[['%']] = Symbol.Percent,
|
||||
[['|']] = Symbol.Pipe,
|
||||
};
|
||||
|
||||
private static readonly (char[] Pattern, Symbol Symbol)[] OrderedSymbols = Symbols
|
||||
|
||||
@@ -19,7 +19,13 @@ public enum BinaryOperator
|
||||
Plus,
|
||||
Minus,
|
||||
Multiply,
|
||||
Divide
|
||||
Divide,
|
||||
Modulo,
|
||||
LeftShift,
|
||||
RightShift,
|
||||
BitwiseAnd,
|
||||
BitwiseXor,
|
||||
BitwiseOr
|
||||
}
|
||||
|
||||
public abstract record ExpressionNode(TypeNode Type) : Node;
|
||||
|
||||
@@ -345,7 +345,50 @@ public sealed class TypeChecker
|
||||
{
|
||||
var boundLeft = CheckExpression(expression.Left);
|
||||
var boundRight = CheckExpression(expression.Right, boundLeft.Type);
|
||||
return new BinaryExpressionNode(boundLeft.Type, boundLeft, CheckBinaryOperator(expression.OperatorSyntax), boundRight);
|
||||
|
||||
var op = expression.Operator switch
|
||||
{
|
||||
BinaryOperatorSyntax.Equal => BinaryOperator.Equal,
|
||||
BinaryOperatorSyntax.NotEqual => BinaryOperator.NotEqual,
|
||||
BinaryOperatorSyntax.GreaterThan => BinaryOperator.GreaterThan,
|
||||
BinaryOperatorSyntax.GreaterThanOrEqual => BinaryOperator.GreaterThanOrEqual,
|
||||
BinaryOperatorSyntax.LessThan => BinaryOperator.LessThan,
|
||||
BinaryOperatorSyntax.LessThanOrEqual => BinaryOperator.LessThanOrEqual,
|
||||
BinaryOperatorSyntax.Plus => BinaryOperator.Plus,
|
||||
BinaryOperatorSyntax.Minus => BinaryOperator.Minus,
|
||||
BinaryOperatorSyntax.Multiply => BinaryOperator.Multiply,
|
||||
BinaryOperatorSyntax.Divide => BinaryOperator.Divide,
|
||||
BinaryOperatorSyntax.Modulo => BinaryOperator.Modulo,
|
||||
BinaryOperatorSyntax.LeftShift => BinaryOperator.LeftShift,
|
||||
BinaryOperatorSyntax.RightShift => BinaryOperator.RightShift,
|
||||
BinaryOperatorSyntax.BitwiseAnd => BinaryOperator.BitwiseAnd,
|
||||
BinaryOperatorSyntax.BitwiseXor => BinaryOperator.BitwiseXor,
|
||||
BinaryOperatorSyntax.BitwiseOr => BinaryOperator.BitwiseOr,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(expression.Operator), expression.Operator, null)
|
||||
};
|
||||
|
||||
var resultingType = op switch
|
||||
{
|
||||
BinaryOperator.Equal => new BoolTypeNode(),
|
||||
BinaryOperator.NotEqual => new BoolTypeNode(),
|
||||
BinaryOperator.GreaterThan => new BoolTypeNode(),
|
||||
BinaryOperator.GreaterThanOrEqual => new BoolTypeNode(),
|
||||
BinaryOperator.LessThan => new BoolTypeNode(),
|
||||
BinaryOperator.LessThanOrEqual => new BoolTypeNode(),
|
||||
BinaryOperator.Plus => boundLeft.Type,
|
||||
BinaryOperator.Minus => boundLeft.Type,
|
||||
BinaryOperator.Multiply => boundLeft.Type,
|
||||
BinaryOperator.Divide => boundLeft.Type,
|
||||
BinaryOperator.Modulo => boundLeft.Type,
|
||||
BinaryOperator.LeftShift => boundLeft.Type,
|
||||
BinaryOperator.RightShift => boundLeft.Type,
|
||||
BinaryOperator.BitwiseAnd => boundLeft.Type,
|
||||
BinaryOperator.BitwiseXor => boundLeft.Type,
|
||||
BinaryOperator.BitwiseOr => boundLeft.Type,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
return new BinaryExpressionNode(resultingType, boundLeft, op, boundRight);
|
||||
}
|
||||
|
||||
private DereferenceNode CheckDereference(DereferenceSyntax expression)
|
||||
@@ -606,7 +649,7 @@ public sealed class TypeChecker
|
||||
|
||||
TypeNode? type = null;
|
||||
|
||||
switch (expression.OperatorSyntax)
|
||||
switch (expression.Operator)
|
||||
{
|
||||
case UnaryOperatorSyntax.Negate:
|
||||
{
|
||||
@@ -633,7 +676,14 @@ public sealed class TypeChecker
|
||||
throw new TypeCheckerException(Diagnostic.Error($"Cannot perform unary operation {expression.Operand} on type {boundOperand.Type}").Build());
|
||||
}
|
||||
|
||||
return new UnaryExpressionNode(type, CheckUnaryOperator(expression.OperatorSyntax), boundOperand);
|
||||
var op = expression.Operator switch
|
||||
{
|
||||
UnaryOperatorSyntax.Negate => UnaryOperator.Negate,
|
||||
UnaryOperatorSyntax.Invert => UnaryOperator.Invert,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(expression.Operator), expression.Operator, null)
|
||||
};
|
||||
|
||||
return new UnaryExpressionNode(type, op, boundOperand);
|
||||
}
|
||||
|
||||
private FuncSignatureNode CheckFuncSignature(FuncSignatureSyntax node)
|
||||
@@ -648,34 +698,6 @@ public sealed class TypeChecker
|
||||
return new FuncSignatureNode(parameters, CheckType(node.ReturnType));
|
||||
}
|
||||
|
||||
private BinaryOperator CheckBinaryOperator(BinaryOperatorSyntax op)
|
||||
{
|
||||
return op switch
|
||||
{
|
||||
BinaryOperatorSyntax.Equal => BinaryOperator.Equal,
|
||||
BinaryOperatorSyntax.NotEqual => BinaryOperator.NotEqual,
|
||||
BinaryOperatorSyntax.GreaterThan => BinaryOperator.GreaterThan,
|
||||
BinaryOperatorSyntax.GreaterThanOrEqual => BinaryOperator.GreaterThanOrEqual,
|
||||
BinaryOperatorSyntax.LessThan => BinaryOperator.LessThan,
|
||||
BinaryOperatorSyntax.LessThanOrEqual => BinaryOperator.LessThanOrEqual,
|
||||
BinaryOperatorSyntax.Plus => BinaryOperator.Plus,
|
||||
BinaryOperatorSyntax.Minus => BinaryOperator.Minus,
|
||||
BinaryOperatorSyntax.Multiply => BinaryOperator.Multiply,
|
||||
BinaryOperatorSyntax.Divide => BinaryOperator.Divide,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(op), op, null)
|
||||
};
|
||||
}
|
||||
|
||||
private UnaryOperator CheckUnaryOperator(UnaryOperatorSyntax op)
|
||||
{
|
||||
return op switch
|
||||
{
|
||||
UnaryOperatorSyntax.Negate => UnaryOperator.Negate,
|
||||
UnaryOperatorSyntax.Invert => UnaryOperator.Invert,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(op), op, null)
|
||||
};
|
||||
}
|
||||
|
||||
private BlockNode CheckBlock(BlockSyntax node, Scope? scope = null)
|
||||
{
|
||||
var statements = new List<StatementNode>();
|
||||
|
||||
Reference in New Issue
Block a user