Parser seems to work
This commit is contained in:
parent
d0249b9a76
commit
5b536be53b
|
@ -12,11 +12,23 @@ public static class AstGenerator {
|
||||||
Variant("Expr",
|
Variant("Expr",
|
||||||
Case("int", "Int"),
|
Case("int", "Int"),
|
||||||
Case("string", "String")),
|
Case("string", "String")),
|
||||||
|
|
||||||
Variant("ParserResult",
|
Variant("ParserResult",
|
||||||
Case("(MixFix.Annotation, IEnumerable<ParserResult>)", "Annotated"),
|
Case("(MixFix.Annotation, ParserResult)", "Annotated"),
|
||||||
|
Case("Lexer.Lexeme", "Terminal"),
|
||||||
|
Case("IEnumerable<ParserResult>", "Productions")),
|
||||||
|
|
||||||
|
Variant("ParserResult2",
|
||||||
|
Case("ValueTuple<MixFix.Associativity, IEnumerable<OperatorOrHole>>", "SamePrecedence")),
|
||||||
|
Variant("OperatorOrHole",
|
||||||
|
Case("ValueTuple<MixFix.Operator, IEnumerable<SamePrecedenceOrTerminal>>", "Operator"),
|
||||||
|
Case("ParserResult2", "Hole")),
|
||||||
|
Variant("SamePrecedenceOrTerminal",
|
||||||
|
Case("ParserResult2", "SamePrecedence"),
|
||||||
Case("Lexer.Lexeme", "Terminal")),
|
Case("Lexer.Lexeme", "Terminal")),
|
||||||
|
|
||||||
Variant("AstNode",
|
Variant("AstNode",
|
||||||
Case("Lexer.Lexeme", "Terminal"),
|
Case("Lexer.Lexeme", "Terminal"),
|
||||||
Case("IEnumerable<AstNode>", "Operator"))));
|
Case("ValueTuple<MixFix.Operator, IEnumerable<AstNode>>", "Operator"))));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ public static class DefaultGrammar {
|
||||||
.WithOperator("multiplicative", LeftAssociative, "int|terminal", S.Times, "int|terminal")
|
.WithOperator("multiplicative", LeftAssociative, "int|terminal", S.Times, "int|terminal")
|
||||||
.WithOperator("terminal", NonAssociative, S.Ident)
|
.WithOperator("terminal", NonAssociative, S.Ident)
|
||||||
// This is the root set of operators
|
// This is the root set of operators
|
||||||
.WithOperator("program", NonAssociative,
|
.WithOperator("program", LeftAssociative,
|
||||||
// "bool" // TODO: this needs aliases
|
// "bool" // TODO: this needs aliases
|
||||||
"equality|terminal", S.And, "equality|terminal");
|
"equality|terminal", S.And, "equality|terminal");
|
||||||
}
|
}
|
|
@ -185,7 +185,9 @@ public static partial class MixFix {
|
||||||
: Paren(l.Count() != 1, l.Select(x => x.Str()).JoinWith(", ")),
|
: Paren(l.Count() != 1, l.Select(x => x.Str()).JoinWith(", ")),
|
||||||
RepeatOnePlus: g => $"{g.Str()}+",
|
RepeatOnePlus: g => $"{g.Str()}+",
|
||||||
Terminal: t => t.Str(),
|
Terminal: t => t.Str(),
|
||||||
Annotated: a => $"Annotated({a.Item1.Str()}, {a.Item2.Str()})"
|
Annotated: a =>
|
||||||
|
//$"Annotated({a.Item1.Str()}, {a.Item2.Str()})"
|
||||||
|
$"~{a.Item2.Str()}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
286
Parser.cs
286
Parser.cs
|
@ -21,10 +21,11 @@ public static partial class Parser {
|
||||||
IImmutableEnumerator<Lexeme> tokens,
|
IImmutableEnumerator<Lexeme> tokens,
|
||||||
Grammar2 grammar
|
Grammar2 grammar
|
||||||
)
|
)
|
||||||
|
//=> Log($"Parser {grammar.ToString()} against {tokens.FirstAndRest().IfSome((first, rest) => first)}", ()
|
||||||
=> grammar.Match(
|
=> grammar.Match(
|
||||||
RepeatOnePlus: g =>
|
RepeatOnePlus: g =>
|
||||||
tokens.FoldMapWhileSome(restI => Parse3(restI, g))
|
tokens.FoldMapWhileSome(restI => Parse3(restI, g))
|
||||||
.If((restN, nodes) => nodes.Count() > 1)
|
.If((restN, nodes) => nodes.Count() >= 1)
|
||||||
.IfSome((restN, nodes) => (restN, ParserResult.Productions(nodes))),
|
.IfSome((restN, nodes) => (restN, ParserResult.Productions(nodes))),
|
||||||
// TODO: to check for ambiguous parses, we can use
|
// TODO: to check for ambiguous parses, we can use
|
||||||
// .Single(…) instead of .First(…).
|
// .Single(…) instead of .First(…).
|
||||||
|
@ -59,10 +60,25 @@ public static partial class Parser {
|
||||||
|
|
||||||
|
|
||||||
// Variant("ParserResult",
|
// Variant("ParserResult",
|
||||||
// Case("(Annotation, IEnumerable<ParserResult>)", "Annotated"),
|
// Case("(MixFix.Annotation, ParserResult)", "Annotated"),
|
||||||
// Case("Lexer.Lexeme", "Terminal"))
|
// Case("Lexer.Lexeme", "Terminal"),
|
||||||
|
// Case("IEnumerable<ParserResult>", "Productions")),
|
||||||
|
|
||||||
// ParserResult = A(SamePrecedence, *) | A(Operator, *) | A(Hole, *)
|
// ParserResult = A(SamePrecedence, *) | A(Operator, repeat|Terminal) | A(Hole, SamePrecedence)
|
||||||
|
|
||||||
|
// Variant("ParserResult",
|
||||||
|
// Case("(MixFix.Annotation, ParserResult)", "Annotated"),
|
||||||
|
// Case("Lexer.Lexeme", "Terminal"),
|
||||||
|
// Case("IEnumerable<ParserResult>", "Productions")),
|
||||||
|
|
||||||
|
// Variant("ParserResult2",
|
||||||
|
// Case("IEnumerable<OperatorOrHole>", "SamePrecedence")),
|
||||||
|
// Variant("OperatorOrHole",
|
||||||
|
// Case("IEnumerable<SamePrecedenceOrTerminal>", "Operator")
|
||||||
|
// Case("Ast.SamePrecedence", "Hole")),
|
||||||
|
// Variant("SamePrecedenceOrTerminal",
|
||||||
|
// Case("Ast.SamePrecedence", "SamePrecedence"),
|
||||||
|
// Case("Lexer.Lexeme", "Terminal")),
|
||||||
|
|
||||||
// Annotated(Hole, lsucc);
|
// Annotated(Hole, lsucc);
|
||||||
// Annotated(Operator, closed, nonAssoc, prefix, postfix, infixl, infixr)
|
// Annotated(Operator, closed, nonAssoc, prefix, postfix, infixl, infixr)
|
||||||
|
@ -75,11 +91,251 @@ public static partial class Parser {
|
||||||
// | ((prefix || infixr) ? R( ((prefix | (lsucc, infixr))["+"], rsucc) ) : Impossible)
|
// | ((prefix || infixr) ? R( ((prefix | (lsucc, infixr))["+"], rsucc) ) : Impossible)
|
||||||
// | ((postfix || infixl) ? L( (lsucc, (postfix || (infixl, rsucc))["+"]) ) : Impossible);
|
// | ((postfix || infixl) ? L( (lsucc, (postfix || (infixl, rsucc))["+"]) ) : Impossible);
|
||||||
|
|
||||||
public static AstNode PostProcess(this ParserResult parserResult) {
|
// We lost some typing information and the structure is scattered around
|
||||||
parserResult.Match(
|
// in Annotation nodes. For now gather everything back into the right
|
||||||
Annotated:
|
// structure after the fact.
|
||||||
)
|
public static ParserResult2 Gather(this ParserResult parserResult)
|
||||||
throw new ParserErrorException("TODO:" + parserResult.ToString());
|
=> parserResult
|
||||||
|
.AsAnnotated
|
||||||
|
.ElseThrow(new ParserErrorException("Internal error: Expected Annotated"))
|
||||||
|
.Pipe(a => a.Item1.AsSamePrecedence
|
||||||
|
.ElseThrow(new ParserErrorException("Internal error: Expected SamePrecedence"))
|
||||||
|
.Pipe(associativity =>
|
||||||
|
ParserResult2.SamePrecedence(
|
||||||
|
(associativity, a.Item2.GatherOperatorOrHole()))));
|
||||||
|
|
||||||
|
public static IEnumerable<OperatorOrHole> GatherOperatorOrHole(this ParserResult parserResult)
|
||||||
|
=> parserResult.Match(
|
||||||
|
Annotated: a => a.Item1.Match(
|
||||||
|
Operator: @operator =>
|
||||||
|
OperatorOrHole.Operator(
|
||||||
|
(@operator, a.Item2.GatherSamePrecedenceOrTerminal()))
|
||||||
|
.Singleton(),
|
||||||
|
Hole: () =>
|
||||||
|
OperatorOrHole.Hole(
|
||||||
|
a.Item2.Gather())
|
||||||
|
.Singleton(),
|
||||||
|
SamePrecedence: associativity =>
|
||||||
|
throw new ParserErrorException("Internal error: Expected Operator or Hole")
|
||||||
|
),
|
||||||
|
Productions: p =>
|
||||||
|
p.SelectMany(GatherOperatorOrHole),
|
||||||
|
Terminal: t =>
|
||||||
|
throw new ParserErrorException("Internal error: Expected Annotated or Productions"));
|
||||||
|
|
||||||
|
public static IEnumerable<SamePrecedenceOrTerminal> GatherSamePrecedenceOrTerminal(this ParserResult parserResult)
|
||||||
|
=> parserResult.Match(
|
||||||
|
Annotated: a => a.Item1.Match(
|
||||||
|
SamePrecedence: associativity =>
|
||||||
|
SamePrecedenceOrTerminal.SamePrecedence(
|
||||||
|
parserResult.Gather())
|
||||||
|
.Singleton(),
|
||||||
|
Hole: () =>
|
||||||
|
throw new ParserErrorException("Internal error: Expected SamePrecedence or Terminal"),
|
||||||
|
Operator: associativity =>
|
||||||
|
throw new ParserErrorException("Internal error: Expected SamePrecedence or Terminal")
|
||||||
|
),
|
||||||
|
Productions: p =>
|
||||||
|
p.SelectMany(GatherSamePrecedenceOrTerminal),
|
||||||
|
Terminal: lexeme =>
|
||||||
|
SamePrecedenceOrTerminal.Terminal(lexeme)
|
||||||
|
.Singleton());
|
||||||
|
|
||||||
|
// ParserResult2 =
|
||||||
|
// | (MixFix.Associativity, IEnumerable<OperatorOrHole>) SamePrecedence
|
||||||
|
// OperatorOrHole =
|
||||||
|
// | (MixFix.Operator, IEnumerable<SamePrecedenceOrTerminal>) Operator
|
||||||
|
// | ParserResult2 Hole
|
||||||
|
// SamePrecedenceOrTerminal =
|
||||||
|
// | ParserResult2 SamePrecedence
|
||||||
|
// | Lexer.Lexeme Terminal
|
||||||
|
|
||||||
|
public static ValueTuple<MixFix.Associativity, IEnumerable<OperatorOrHole>> Get(this ParserResult2 parserResult)
|
||||||
|
=> parserResult.Match(SamePrecedence: p =>p);
|
||||||
|
|
||||||
|
/*
|
||||||
|
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> e, Func<T, T, bool> predicate) {
|
||||||
|
var currentList = ImmutableStack<T>.Empty;
|
||||||
|
T prev = default(T);
|
||||||
|
var first = true;
|
||||||
|
foreach (var x in e) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
if (predicate(prev, x)) {
|
||||||
|
yield return currentList.Reverse();
|
||||||
|
currentList = ImmutableStack<T>.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentList = currentList.Push(x);
|
||||||
|
prev = x;
|
||||||
|
}
|
||||||
|
yield return currentList.Reverse();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> e, Func<T, bool> predicate) {
|
||||||
|
var currentList = ImmutableStack<T>.Empty;
|
||||||
|
foreach (var x in e) {
|
||||||
|
if (predicate(x)) {
|
||||||
|
// yield the elements
|
||||||
|
yield return currentList.Reverse();
|
||||||
|
currentList = ImmutableStack<T>.Empty;
|
||||||
|
// yield the separator
|
||||||
|
yield return x.Singleton();
|
||||||
|
} else {
|
||||||
|
currentList = currentList.Push(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// yield the last (possibly empty) unclosed batch of elements
|
||||||
|
yield return currentList.Reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// TODO: use an Either<T, U> class instead of an Option with another Func.
|
||||||
|
public static IEnumerable<IEnumerable<U>> Split<T, U>(this IEnumerable<T> e, Func<T, Option<U>> predicate, Func<IEnumerable<T>, U> transform) {
|
||||||
|
var currentList = ImmutableStack<T>.Empty;
|
||||||
|
foreach (var x in e) {
|
||||||
|
var p = predicate(x);
|
||||||
|
if (p.IsSome) {
|
||||||
|
// yield the elements
|
||||||
|
yield return transform(currentList.Reverse());
|
||||||
|
currentList = ImmutableStack.Empty;
|
||||||
|
// yield the separator
|
||||||
|
yield return p.AsSome.ElseThrow("impossible");
|
||||||
|
} else {
|
||||||
|
currentList = currentList.Push(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// yield the last (possibly empty) unclosed batch of elements
|
||||||
|
yield return transform(currentList.Reverse());
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public static A FoldLeft3<T,A>(this IEnumerable<T> ie, Func<T,T,T,A> init, Func<A,T,T,A> f) {
|
||||||
|
var e = ie.GetEnumerator();
|
||||||
|
e.MoveNext();
|
||||||
|
T a = e.Current;
|
||||||
|
e.MoveNext();
|
||||||
|
T b = e.Current;
|
||||||
|
e.MoveNext();
|
||||||
|
T c = e.Current;
|
||||||
|
A acc = init(a, b, c);
|
||||||
|
while (e.MoveNext()) {
|
||||||
|
T x = e.Current;
|
||||||
|
e.MoveNext();
|
||||||
|
T y = e.Current;
|
||||||
|
acc = f(acc, x, y);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static A FoldRight3<T,A>(this IEnumerable<T> ie, Func<T,T,T,A> init, Func<T,T,A,A> f) {
|
||||||
|
var e = ie.Reverse().GetEnumerator();
|
||||||
|
e.MoveNext();
|
||||||
|
T a = e.Current;
|
||||||
|
e.MoveNext();
|
||||||
|
T b = e.Current;
|
||||||
|
e.MoveNext();
|
||||||
|
T c = e.Current;
|
||||||
|
A acc = init(c, b, a);
|
||||||
|
while (e.MoveNext()) {
|
||||||
|
T x = e.Current;
|
||||||
|
e.MoveNext();
|
||||||
|
T y = e.Current;
|
||||||
|
acc = f(y, x, acc);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AstNode PostProcess(this SamePrecedenceOrTerminal samePrecedenceOrTerminal)
|
||||||
|
=> samePrecedenceOrTerminal.Match(
|
||||||
|
SamePrecedence: x => PostProcess(x),
|
||||||
|
// TODO: just writing Terminal: AstNode.Terminal causes a null exception
|
||||||
|
Terminal: x => AstNode.Terminal(x));
|
||||||
|
|
||||||
|
public static IEnumerable<AstNode> PostProcess(this OperatorOrHole operatorOrHole)
|
||||||
|
=> operatorOrHole.Match(
|
||||||
|
Operator: o => o.Item2.Select(PostProcess),
|
||||||
|
Hole: h => h.PostProcess().Singleton());
|
||||||
|
|
||||||
|
|
||||||
|
public static AstNode PostProcess(this ParserResult2 parserResult) {
|
||||||
|
// Let's start with right associativity
|
||||||
|
// TODO: handle other associativities
|
||||||
|
|
||||||
|
// We flatten by converting to a sequence of SamePrecedenceOrTerminal
|
||||||
|
|
||||||
|
// turn this: h h o h o o o h h o o h
|
||||||
|
// into this: (h h) (o) (h) (o) () (o) () (o) (h h) (o) () (o) (h)
|
||||||
|
// and this: o h o o o h h o o
|
||||||
|
// into this: () (o) (h) (o) () (o) () (o) (h h) (o) () (o) ()
|
||||||
|
// i.e. always have a (possibly empty) list on both ends.
|
||||||
|
|
||||||
|
var split = parserResult.Get().Item2.Split(x => x.IsOperator);
|
||||||
|
|
||||||
|
return parserResult.Get().Item1.Match(
|
||||||
|
NonAssociative: () => {
|
||||||
|
if (split.Count() != 3) {
|
||||||
|
throw new ParserErrorException($"Internal error: NonAssociative operator within group of {split.Count()} elements, expected exactly 3");
|
||||||
|
} else {
|
||||||
|
var @operator =
|
||||||
|
split.ElementAt(1)
|
||||||
|
.Single().ElseThrow(new Exception("impossible"))
|
||||||
|
.AsOperator.ElseThrow(new Exception("impossible"));
|
||||||
|
return AstNode.Operator(
|
||||||
|
(@operator.Item1,
|
||||||
|
split.ElementAt(0).SelectMany(PostProcess)
|
||||||
|
.Concat(split.ElementAt(1).SelectMany(PostProcess))
|
||||||
|
.Concat(split.ElementAt(2).SelectMany(PostProcess))));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RightAssociative: () =>
|
||||||
|
split.FoldRight3(
|
||||||
|
// Last group of three
|
||||||
|
(hsl, o, hsr) => {
|
||||||
|
var @operator = o
|
||||||
|
.Single().ElseThrow(new Exception("impossible"))
|
||||||
|
.AsOperator.ElseThrow(new Exception("impossible"));
|
||||||
|
return AstNode.Operator(
|
||||||
|
(@operator.Item1,
|
||||||
|
hsl.SelectMany(PostProcess)
|
||||||
|
.Concat(o.SelectMany(PostProcess))
|
||||||
|
.Concat(hsr.SelectMany(PostProcess))));
|
||||||
|
},
|
||||||
|
// Subsequent groups of two starting with accumulator
|
||||||
|
(hsl, o, a) => {
|
||||||
|
var @operator = o
|
||||||
|
.Single().ElseThrow(new Exception("impossible"))
|
||||||
|
.AsOperator.ElseThrow(new Exception("impossible"));
|
||||||
|
return AstNode.Operator(
|
||||||
|
(@operator.Item1,
|
||||||
|
hsl.SelectMany(PostProcess)
|
||||||
|
.Concat(o.SelectMany(PostProcess))
|
||||||
|
.Concat(a)));
|
||||||
|
}),
|
||||||
|
LeftAssociative: () =>
|
||||||
|
split.FoldLeft3(
|
||||||
|
// Fist group of three
|
||||||
|
(hsl, o, hsr) => {
|
||||||
|
var @operator = o
|
||||||
|
.Single().ElseThrow(new Exception("impossible"))
|
||||||
|
.AsOperator.ElseThrow(new Exception("impossible"));
|
||||||
|
return AstNode.Operator(
|
||||||
|
(@operator.Item1,
|
||||||
|
hsl.SelectMany(PostProcess)
|
||||||
|
.Concat(o.SelectMany(PostProcess))
|
||||||
|
.Concat(hsr.SelectMany(PostProcess))));
|
||||||
|
},
|
||||||
|
// Subsequent groups of two starting with accumulator
|
||||||
|
(a, o, hsr) => {
|
||||||
|
var @operator = o
|
||||||
|
.Single().ElseThrow(new Exception("impossible"))
|
||||||
|
.AsOperator.ElseThrow(new Exception("impossible"));
|
||||||
|
return AstNode.Operator(
|
||||||
|
(@operator.Item1,
|
||||||
|
a.Singleton()
|
||||||
|
.Concat(o.SelectMany(PostProcess))
|
||||||
|
.Concat(hsr.SelectMany(PostProcess))));
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -186,16 +442,16 @@ public static partial class Parser {
|
||||||
Parse3
|
Parse3
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Log(grammar.ToString());
|
||||||
|
|
||||||
return P(Lexer.Lex(source), grammar)
|
return P(Lexer.Lex(source), grammar)
|
||||||
.IfSome((rest, result) => (rest, PostProcess(result)));
|
.IfSome((rest, result) => (rest, result.Gather().PostProcess()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Ast.Expr Parse(string source) {
|
public static Ast.Expr Parse(string source) {
|
||||||
Parse2(source).ToString();
|
Log("");
|
||||||
//Log("");
|
Log("Parsed:" + Parse2(source).ToString());
|
||||||
//Log("" + Parse2(source).ToString());
|
Log("");
|
||||||
//Log("");
|
|
||||||
Environment.Exit(0);
|
|
||||||
|
|
||||||
return Lexer.Lex(source)
|
return Lexer.Lex(source)
|
||||||
.SelectMany(lexeme =>
|
.SelectMany(lexeme =>
|
||||||
|
|
|
@ -32,6 +32,9 @@ public static class RecordGenerator {
|
||||||
var F = @field.Key;
|
var F = @field.Key;
|
||||||
var Ty = @field.Value;
|
var Ty = @field.Value;
|
||||||
w($" this.{F} = {F};");
|
w($" this.{F} = {F};");
|
||||||
|
w($" if (object.ReferenceEquals({F}, null)) {{");
|
||||||
|
w($" throw new Exception(\"Argument {Ty} {F} to {name} was null.\");");
|
||||||
|
w($" }}");
|
||||||
}
|
}
|
||||||
w($" this.hashCode = Equality.HashCode(\"{name}\",");
|
w($" this.hashCode = Equality.HashCode(\"{name}\",");
|
||||||
w(String.Join(",\n", record.Select(@field =>
|
w(String.Join(",\n", record.Select(@field =>
|
||||||
|
|
|
@ -122,6 +122,11 @@ public static class VariantGenerator {
|
||||||
private static void CaseConstructor(this Action<string> w, string qualifier, string name, string C, string Ty) {
|
private static void CaseConstructor(this Action<string> w, string qualifier, string name, string C, string Ty) {
|
||||||
w($" public {C}({Ty == null ? "" : $"{Ty} value"}) {{");
|
w($" public {C}({Ty == null ? "" : $"{Ty} value"}) {{");
|
||||||
w($" {Ty == null ? "" : $"this.value = value; "}");
|
w($" {Ty == null ? "" : $"this.value = value; "}");
|
||||||
|
if (Ty != null) {
|
||||||
|
w($" if (object.ReferenceEquals(value, null)) {{");
|
||||||
|
w($" throw new Exception(\"Argument of type {Ty} to {name}.{C} was null.\");");
|
||||||
|
w($" }}");
|
||||||
|
}
|
||||||
if (Ty == null) {
|
if (Ty == null) {
|
||||||
w($" this.hashCode = HashCode.Combine(\"{C}\");");
|
w($" this.hashCode = HashCode.Combine(\"{C}\");");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
true && false
|
|
1
Tests/006-eq.e
Normal file
1
Tests/006-eq.e
Normal file
|
@ -0,0 +1 @@
|
||||||
|
40 + 2 == 40 + 1 + 1 && true
|
1
Tests/006-eq.o
Normal file
1
Tests/006-eq.o
Normal file
|
@ -0,0 +1 @@
|
||||||
|
true
|
|
@ -35,6 +35,21 @@ public static class ToStringImplementations {
|
||||||
public static string Str<T>(this IEnumerable<Ast.ParserResult> e)
|
public static string Str<T>(this IEnumerable<Ast.ParserResult> e)
|
||||||
=> $"IEnumerab({e.Select(x => x.Str<Ast.ParserResult>()).JoinWith(", ")})";
|
=> $"IEnumerab({e.Select(x => x.Str<Ast.ParserResult>()).JoinWith(", ")})";
|
||||||
|
|
||||||
|
public static string Str<T>(this IEnumerable<Ast.OperatorOrHole> e)
|
||||||
|
=> $"IEnumerab({e.Select(x => x.Str<Ast.OperatorOrHole>()).JoinWith(", ")})";
|
||||||
|
|
||||||
|
public static string Str<T>(this ValueTuple<MixFix.Associativity, IEnumerable<Ast.OperatorOrHole>> t)
|
||||||
|
=> $"({t.Item1.Str()}, {t.Item2.Str<IEnumerable<Ast.OperatorOrHole>>()})";
|
||||||
|
|
||||||
|
public static string Str<T>(this IEnumerable<Ast.SamePrecedenceOrTerminal> e)
|
||||||
|
=> $"IEnumerab({e.Select(x => x.Str<Ast.SamePrecedenceOrTerminal>()).JoinWith(", ")})";
|
||||||
|
|
||||||
|
public static string Str<T>(this ValueTuple<MixFix.Operator, IEnumerable<Ast.SamePrecedenceOrTerminal>> t)
|
||||||
|
=> $"({t.Item1.Str()}, {t.Item2.Str<IEnumerable<Ast.SamePrecedenceOrTerminal>>()})";
|
||||||
|
|
||||||
|
public static string Str<T>(this ValueTuple<MixFix.Operator, IEnumerable<Ast.AstNode>> t)
|
||||||
|
=> $"({t.Item1.Str()}, {t.Item2.Str<IEnumerable<Ast.AstNode>>()})";
|
||||||
|
|
||||||
public static string Str<Grammar>(this ImmutableDictionary<string,Grammar> h)
|
public static string Str<Grammar>(this ImmutableDictionary<string,Grammar> h)
|
||||||
=> $"ImmutableDictionary(\n{h.Select(x => $" {x.Key.Str<string>()}:{x.Value.Str<Grammar>()}").JoinWith(",\n")}\n)";
|
=> $"ImmutableDictionary(\n{h.Select(x => $" {x.Key.Str<string>()}:{x.Value.Str<Grammar>()}").JoinWith(",\n")}\n)";
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user