WIP
This commit is contained in:
parent
5b536be53b
commit
de12594067
|
@ -13,6 +13,10 @@ public static class AstGenerator {
|
||||||
Case("int", "Int"),
|
Case("int", "Int"),
|
||||||
Case("string", "String")),
|
Case("string", "String")),
|
||||||
|
|
||||||
|
Variant("Val",
|
||||||
|
Case("int", "Int"),
|
||||||
|
Case("string", "String")),
|
||||||
|
|
||||||
Variant("ParserResult",
|
Variant("ParserResult",
|
||||||
Case("(MixFix.Annotation, ParserResult)", "Annotated"),
|
Case("(MixFix.Annotation, ParserResult)", "Annotated"),
|
||||||
Case("Lexer.Lexeme", "Terminal"),
|
Case("Lexer.Lexeme", "Terminal"),
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
namespace Compilers {
|
namespace Compilers {
|
||||||
public class JS {
|
public class JS {
|
||||||
public static string Compile(Ast.Expr source) {
|
public static string Compile(Ast.AstNode source) {
|
||||||
return "process.stdout.write(String("
|
return "process.stdout.write(String("
|
||||||
+ source.Match(
|
+ "\"no JS for now\""
|
||||||
|
/* + source.Match(
|
||||||
Int: i => i.ToString(),
|
Int: i => i.ToString(),
|
||||||
String: s => $"'{s.ToString()}'"
|
String: s => $"'{s.ToString()}'"
|
||||||
)
|
)*/
|
||||||
+ "));";
|
+ "));";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,22 @@ using PrecedenceDAG = ImmutableDefaultDictionary<string, MixFix.DAGNode>;
|
||||||
using static Global;
|
using static Global;
|
||||||
using static MixFix;
|
using static MixFix;
|
||||||
using static MixFix.Associativity;
|
using static MixFix.Associativity;
|
||||||
|
using static MixFix.Semantics;
|
||||||
|
|
||||||
public static class DefaultGrammar {
|
public static class DefaultGrammar {
|
||||||
public static PrecedenceDAG DefaultPrecedenceDAG
|
public static PrecedenceDAG DefaultPrecedenceDAG
|
||||||
= EmptyPrecedenceDAG
|
= EmptyPrecedenceDAG
|
||||||
.WithOperator("bool", NonAssociative, "equality|terminal", S.And, "equality|terminal")
|
.WithOperator("bool", Unsupported, NonAssociative, "equality|terminal", S.And, "equality|terminal")
|
||||||
.WithOperator("equality", NonAssociative, "int|terminal|additive|multiplicative", S.Eq, "int|terminal|additive|multiplicative")
|
.WithOperator("equality", Unsupported, NonAssociative, "int|terminal", S.Eq, "int|terminal") // |additive|multiplicative
|
||||||
.WithOperator("int", NonAssociative, S.Int)
|
.WithOperator("int", LiteralInt, NonAssociative, S.Int)
|
||||||
.WithOperator("additive", LeftAssociative, "int|terminal|multiplicative", S.Plus, "int|terminal|multiplicative")
|
.WithOperator("additive", Unsupported, LeftAssociative, "int|terminal|multiplicative", S.Plus, "int|terminal|multiplicative")
|
||||||
.WithOperator("multiplicative", LeftAssociative, "int|terminal", S.Times, "int|terminal")
|
.WithOperator("multiplicative", Unsupported, LeftAssociative, "int|terminal", S.Times, "int|terminal")
|
||||||
.WithOperator("terminal", NonAssociative, S.Ident)
|
.WithOperator("terminal", Unsupported, NonAssociative, S.Ident)
|
||||||
// This is the root set of operators
|
// This is the root set of operators
|
||||||
.WithOperator("program", LeftAssociative,
|
// TODO: this needs aliases
|
||||||
// "bool" // TODO: this needs aliases
|
.WithOperator("prog", Unsupported, LeftAssociative, "equality|terminal", S.And, "equality|terminal")
|
||||||
"equality|terminal", S.And, "equality|terminal");
|
.WithOperator("prog", LiteralInt, NonAssociative, S.Int)
|
||||||
|
.WithOperator("prog", LiteralString, NonAssociative, S.StringOpen, S.String, S.StringClose)
|
||||||
|
.WithOperator("program", Program, NonAssociative, S.StartOfInput, "prog", S.EndOfInput)
|
||||||
|
;
|
||||||
}
|
}
|
42
Evaluator.cs
42
Evaluator.cs
|
@ -1,10 +1,42 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Immutable;
|
||||||
|
using static Global;
|
||||||
|
|
||||||
public class Evaluator {
|
public class Evaluator {
|
||||||
public static string Evaluate(Ast.Expr source) {
|
public static string Evaluate(Ast.AstNode source)
|
||||||
return source.Match(
|
// => Log(source.Str(), ()
|
||||||
Int: i => i.ToString(),
|
=> source.Match(
|
||||||
String: s => s.ToString()
|
Operator: o => o.Item1.semantics.Match(
|
||||||
);
|
// The wrapper around the whole program:
|
||||||
|
Program: () => {
|
||||||
|
if (o.Item2.Count() != 3) {
|
||||||
|
throw new RuntimeErrorException("The Program wrapper should contain two parts: StartOfInput, prog and EndOfInput");
|
||||||
}
|
}
|
||||||
|
// TODO: check that the last token is indeed Program
|
||||||
|
return Evaluate(o.Item2.ElementAt(1));
|
||||||
|
},
|
||||||
|
LiteralInt: () =>
|
||||||
|
o.Item2
|
||||||
|
.Single()
|
||||||
|
.ElseThrow(
|
||||||
|
new RuntimeErrorException("LiteralInt should contain a single lexeme"))
|
||||||
|
.AsTerminal
|
||||||
|
.ElseThrow(
|
||||||
|
new RuntimeErrorException("LiteralInt's contents should be a lexeme"))
|
||||||
|
.lexeme,
|
||||||
|
LiteralString: () => {
|
||||||
|
if (o.Item2.Count() != 3) {
|
||||||
|
throw new RuntimeErrorException("LiteralString should contain three lexemes: OpenString, String and CloseString");
|
||||||
|
}
|
||||||
|
// TODO: check that the open & close are indeed that
|
||||||
|
return o.Item2.ElementAt(1)
|
||||||
|
.AsTerminal.ElseThrow(
|
||||||
|
new RuntimeErrorException("LiteralInt's contents should be a lexeme"))
|
||||||
|
.lexeme;
|
||||||
|
},
|
||||||
|
Unsupported: () => throw new RuntimeErrorException($"Unsupported opeartor {o}, sorry.")),
|
||||||
|
Terminal: t => t.lexeme/*TODO*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: for typeclass resolution, ask that functions have their parameters and return types annotated. This annotation is added to the values at run-time, which allows to dispatch based on the annotation rather than on the actual value.
|
// Note: for typeclass resolution, ask that functions have their parameters and return types annotated. This annotation is added to the values at run-time, which allows to dispatch based on the annotation rather than on the actual value.
|
|
@ -16,6 +16,10 @@ public class LexerErrorException : UserErrorException {
|
||||||
public LexerErrorException(string e) : base("Lexer error: " + e) {}
|
public LexerErrorException(string e) : base("Lexer error: " + e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class RuntimeErrorException : UserErrorException {
|
||||||
|
public RuntimeErrorException(string e) : base("Runtime error: " + e) {}
|
||||||
|
}
|
||||||
|
|
||||||
public class TestFailedException : UserErrorException {
|
public class TestFailedException : UserErrorException {
|
||||||
public TestFailedException(string e) : base("Test failed: " + e) {}
|
public TestFailedException(string e) : base("Test failed: " + e) {}
|
||||||
}
|
}
|
8
Lexer.cs
8
Lexer.cs
|
@ -64,7 +64,7 @@ public static partial class Lexer {
|
||||||
public static ImmutableList<Rule> Default = ImmutableList(
|
public static ImmutableList<Rule> Default = ImmutableList(
|
||||||
Rule(S.Space, C.DecimalDigitNumber, S.Int),
|
Rule(S.Space, C.DecimalDigitNumber, S.Int),
|
||||||
Rule(S.Space, C.SpaceSeparator, S.Space),
|
Rule(S.Space, C.SpaceSeparator, S.Space),
|
||||||
Rule(S.Space, EOF, S.End),
|
Rule(S.Space, EOF, S.EndOfInput, S.End),
|
||||||
Rule(S.Space, '"', S.StringOpen, S.String),
|
Rule(S.Space, '"', S.StringOpen, S.String),
|
||||||
Rule(S.Space, '=', S.Eq),
|
Rule(S.Space, '=', S.Eq),
|
||||||
Rule(S.Eq, '=', S.Eq, S.Space),
|
Rule(S.Eq, '=', S.Eq, S.Space),
|
||||||
|
@ -242,8 +242,10 @@ public static partial class Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IImmutableEnumerator<Lexeme> Lex(string source)
|
public static IImmutableEnumerator<Lexeme> Lex(string source)
|
||||||
=> Lex1(source)
|
=> new Lexeme(S.StartOfInput, "").ImSingleton()
|
||||||
|
.Concat(
|
||||||
|
Lex1(source)
|
||||||
.Flatten()
|
.Flatten()
|
||||||
//.Lazy(SkipInitialEmptyWhitespace.Eq)
|
//.Lazy(SkipInitialEmptyWhitespace.Eq)
|
||||||
.Lazy(DiscardWhitespace.Eq);
|
.Lazy(DiscardWhitespace.Eq));
|
||||||
}
|
}
|
|
@ -15,7 +15,9 @@ public static class LexerGenerator {
|
||||||
// grapheme clusters
|
// grapheme clusters
|
||||||
Field("string", "lexeme")),
|
Field("string", "lexeme")),
|
||||||
Variant("S",
|
Variant("S",
|
||||||
|
Case("StartOfInput"),
|
||||||
Case("End"),
|
Case("End"),
|
||||||
|
Case("EndOfInput"),
|
||||||
Case("Space"),
|
Case("Space"),
|
||||||
Case("Int"),
|
Case("Int"),
|
||||||
Case("Decimal"),
|
Case("Decimal"),
|
||||||
|
|
16
MixFix.cs
16
MixFix.cs
|
@ -401,10 +401,11 @@ public static partial class MixFix {
|
||||||
return precedenceDAG.lens[@operator.precedenceGroup].Add(@operator);
|
return precedenceDAG.lens[@operator.precedenceGroup].Add(@operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PrecedenceDAG WithOperator(this PrecedenceDAG precedenceDAG, string precedenceGroup, Associativity associativity, params Part[] parts)
|
public static PrecedenceDAG WithOperator(this PrecedenceDAG precedenceDAG, string precedenceGroup, Semantics semantics, Associativity associativity, params Part[] parts)
|
||||||
=> precedenceDAG.With(
|
=> precedenceDAG.With(
|
||||||
new Operator(
|
new Operator(
|
||||||
precedenceGroup: precedenceGroup,
|
precedenceGroup: precedenceGroup,
|
||||||
|
semantics: semantics,
|
||||||
associativity: associativity,
|
associativity: associativity,
|
||||||
parts: parts.ToImmutableList()));
|
parts: parts.ToImmutableList()));
|
||||||
|
|
||||||
|
@ -436,7 +437,6 @@ public static partial class MixFix {
|
||||||
Func<Grammar1, Grammar1> L = g => SamePrecedence(Associativity.LeftAssociative, g);
|
Func<Grammar1, Grammar1> L = g => SamePrecedence(Associativity.LeftAssociative, g);
|
||||||
Func<Grammar1, Grammar1> N = g => SamePrecedence(Associativity.NonAssociative, g);
|
Func<Grammar1, Grammar1> N = g => SamePrecedence(Associativity.NonAssociative, g);
|
||||||
Func<Grammar1, Grammar1> H = g => Grammar1.Annotated((Annotation.Hole, g));
|
Func<Grammar1, Grammar1> H = g => Grammar1.Annotated((Annotation.Hole, g));
|
||||||
var Impossible = Grammar1.Impossible;
|
|
||||||
|
|
||||||
var lsucc = H(node.leftmostHole_.ToGrammar1());
|
var lsucc = H(node.leftmostHole_.ToGrammar1());
|
||||||
var rsucc = H(node.rightmostHole_.ToGrammar1());
|
var rsucc = H(node.rightmostHole_.ToGrammar1());
|
||||||
|
@ -448,12 +448,10 @@ public static partial class MixFix {
|
||||||
var infixr = node.infixRightAssociative.ToGrammar1();
|
var infixr = node.infixRightAssociative.ToGrammar1();
|
||||||
|
|
||||||
return
|
return
|
||||||
// TODO: we can normally remove the ?: checks, as the constructors for grammars
|
N(closed)
|
||||||
// now coalesce Impossible cases in the correct way.
|
| N( (lsucc, nonAssoc, rsucc) )
|
||||||
(closed ? N(closed) : Impossible)
|
| R( ((prefix | (lsucc, infixr))["+"], rsucc) )
|
||||||
| (nonAssoc ? N( (lsucc, nonAssoc, rsucc) ) : Impossible)
|
| L( (lsucc, (postfix || (infixl, rsucc))["+"]) );
|
||||||
| ((prefix || infixr) ? R( ((prefix | (lsucc, infixr))["+"], rsucc) ) : Impossible)
|
|
||||||
| ((postfix || infixl) ? L( (lsucc, (postfix || (infixl, rsucc))["+"]) ) : Impossible);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EquatableDictionary<string, Grammar1> ToGrammar1(this PrecedenceDAG precedenceDAG)
|
public static EquatableDictionary<string, Grammar1> ToGrammar1(this PrecedenceDAG precedenceDAG)
|
||||||
|
@ -468,7 +466,7 @@ public static partial class MixFix {
|
||||||
Grammar1 lr = null;
|
Grammar1 lr = null;
|
||||||
try {
|
try {
|
||||||
lr = labeled[r];
|
lr = labeled[r];
|
||||||
} catch (Exception e) {
|
} catch (Exception) {
|
||||||
throw new ParserExtensionException($"Internal error: could not find node {r} in labeled grammar. It only contains labels for: {labeled.Select(kvp => kvp.Key.ToString()).JoinWith(", ")}.");
|
throw new ParserExtensionException($"Internal error: could not find node {r} in labeled grammar. It only contains labels for: {labeled.Select(kvp => kvp.Key.ToString()).JoinWith(", ")}.");
|
||||||
}
|
}
|
||||||
return recur(labeled[r], labeled);
|
return recur(labeled[r], labeled);
|
||||||
|
|
|
@ -49,10 +49,17 @@ public static class ParserGenerator {
|
||||||
Case("RightAssociative")),
|
Case("RightAssociative")),
|
||||||
|
|
||||||
Record("Operator",
|
Record("Operator",
|
||||||
|
Field("Semantics", "semantics"),
|
||||||
Field("PrecedenceGroupName", "precedenceGroup"),
|
Field("PrecedenceGroupName", "precedenceGroup"),
|
||||||
Field("Associativity", "associativity"),
|
Field("Associativity", "associativity"),
|
||||||
Field("ImmutableList<Part>", "parts")),
|
Field("ImmutableList<Part>", "parts")),
|
||||||
|
|
||||||
|
Variant("Semantics",
|
||||||
|
Case("Program"),
|
||||||
|
Case("LiteralInt"),
|
||||||
|
Case("LiteralString"),
|
||||||
|
Case("Unsupported")),
|
||||||
|
|
||||||
Variant("Part",
|
Variant("Part",
|
||||||
Case("S", "Name"),
|
Case("S", "Name"),
|
||||||
Case("ImmutableHashSet<PrecedenceGroupName>", "Hole")),
|
Case("ImmutableHashSet<PrecedenceGroupName>", "Hole")),
|
||||||
|
|
133
Parser.cs
133
Parser.cs
|
@ -29,67 +29,33 @@ public static partial class Parser {
|
||||||
.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(…).
|
||||||
Or: l =>
|
Or: l => {
|
||||||
l.First(g => Parse3(tokens, g)),
|
var i = 0;
|
||||||
|
return l.First(g => {
|
||||||
|
i++;
|
||||||
|
//Log($"{i}/{l.Count()}: trying…");
|
||||||
|
var res = Parse3(tokens, g);
|
||||||
|
//Log($"{i}/{l.Count()}: {res}");
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
},
|
||||||
Sequence: l =>
|
Sequence: l =>
|
||||||
l.BindFoldMap(tokens, (restI, g) => Parse3(restI, g))
|
l.BindFoldMap(tokens, (restI, g) => Parse3(restI, g))
|
||||||
.IfSome((restN, nodes) => (restN, ParserResult.Productions(nodes))),
|
.IfSome((restN, nodes) => (restN, ParserResult.Productions(nodes))),
|
||||||
Terminal: t =>
|
Terminal: t => {
|
||||||
// TODO: move the FirstAndRest here!
|
var attempt = tokens
|
||||||
tokens
|
|
||||||
.FirstAndRest()
|
.FirstAndRest()
|
||||||
// When EOF is reached, the parser can't accept this derivation.
|
// When EOF is reached, the parser can't accept this derivation.
|
||||||
.If((first, rest) => first.state.Equals(t))
|
.If((first, rest) => first.state.Equals(t))
|
||||||
.IfSome((first, rest) => (rest, ParserResult.Terminal(first))),
|
.IfSome((first, rest) => (rest, ParserResult.Terminal(first)));
|
||||||
|
/*if (attempt.IsNone) {
|
||||||
|
Log($"failed to match {tokens.FirstAndRest().IfSome((first, rest) => first)} against terminal {t}.");
|
||||||
|
}*/
|
||||||
|
return attempt;
|
||||||
|
},
|
||||||
Annotated: a =>
|
Annotated: a =>
|
||||||
// TODO: use the annotation to give some shape to these lists
|
|
||||||
Parse3(tokens, a.Item2).IfSome((rest, g) =>
|
Parse3(tokens, a.Item2).IfSome((rest, g) =>
|
||||||
(rest, ParserResult.Annotated((a.Item1, g)))));
|
(rest, ParserResult.Annotated((a.Item1, g)))));
|
||||||
// TODO: at the top-level, check that the lexemes
|
|
||||||
// are empty if the parser won't accept anything else.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Variant("ParserResult",
|
|
||||||
// Case("(MixFix.Annotation, ParserResult)", "Annotated"),
|
|
||||||
// Case("Lexer.Lexeme", "Terminal"),
|
|
||||||
// Case("IEnumerable<ParserResult>", "Productions")),
|
|
||||||
|
|
||||||
// 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(Operator, closed, nonAssoc, prefix, postfix, infixl, infixr)
|
|
||||||
|
|
||||||
// return
|
|
||||||
// // TODO: we can normally remove the ?: checks, as the constructors for grammars
|
|
||||||
// // now coalesce Impossible cases in the correct way.
|
|
||||||
// (closed ? N(closed) : Impossible)
|
|
||||||
// | (nonAssoc ? N( (lsucc, nonAssoc, rsucc) ) : Impossible)
|
|
||||||
// | ((prefix || infixr) ? R( ((prefix | (lsucc, infixr))["+"], rsucc) ) : Impossible)
|
|
||||||
// | ((postfix || infixl) ? L( (lsucc, (postfix || (infixl, rsucc))["+"]) ) : Impossible);
|
|
||||||
|
|
||||||
// We lost some typing information and the structure is scattered around
|
// We lost some typing information and the structure is scattered around
|
||||||
// in Annotation nodes. For now gather everything back into the right
|
// in Annotation nodes. For now gather everything back into the right
|
||||||
|
@ -430,7 +396,7 @@ public static partial class Parser {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static Option<ValueTuple<IImmutableEnumerator<Lexeme>, AstNode>> Parse2(string source) {
|
public static ValueTuple<IImmutableEnumerator<Lexeme>, AstNode> Parse2(string source) {
|
||||||
Grammar2 grammar =
|
Grammar2 grammar =
|
||||||
DefaultGrammar.DefaultPrecedenceDAG.ToGrammar2();
|
DefaultGrammar.DefaultPrecedenceDAG.ToGrammar2();
|
||||||
//Log(grammar.Str());
|
//Log(grammar.Str());
|
||||||
|
@ -442,42 +408,37 @@ public static partial class Parser {
|
||||||
Parse3
|
Parse3
|
||||||
);
|
);
|
||||||
|
|
||||||
Log(grammar.ToString());
|
var lexSrc = Lexer.Lex(source);
|
||||||
|
|
||||||
return P(Lexer.Lex(source), grammar)
|
/*lexSrc
|
||||||
.IfSome((rest, result) => (rest, result.Gather().PostProcess()));
|
.ToIEnumerable()
|
||||||
|
.Select(c => c.state.ToString())
|
||||||
|
.JoinWith(" ")
|
||||||
|
.Pipe(x => Log(x));*/
|
||||||
|
|
||||||
|
//Log("");
|
||||||
|
|
||||||
|
var parsed = P(lexSrc, grammar)
|
||||||
|
.IfSome((rest, result) => (rest, result.Gather().PostProcess()))
|
||||||
|
.ElseThrow(() => new ParserErrorException("Parse error."));
|
||||||
|
|
||||||
|
parsed.Item1.FirstAndRest().IfSome(
|
||||||
|
(first, rest) => {
|
||||||
|
lexSrc
|
||||||
|
.ToIEnumerable()
|
||||||
|
.TakeUntil(c => c.Equals(parsed.Item1))
|
||||||
|
.Select(c => c.lexeme.ToString())
|
||||||
|
.JoinWith(" ")
|
||||||
|
.Pipe(x => throw new ParserErrorException(
|
||||||
|
$"Trailing rubbish: {x}."));
|
||||||
|
return Unit.unit;
|
||||||
|
});
|
||||||
|
|
||||||
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Ast.Expr Parse(string source) {
|
public static Ast.AstNode Parse(string source)
|
||||||
Log("");
|
=> Parse2(source).Item2;
|
||||||
Log("Parsed:" + Parse2(source).ToString());
|
|
||||||
Log("");
|
|
||||||
|
|
||||||
return Lexer.Lex(source)
|
|
||||||
.SelectMany(lexeme =>
|
|
||||||
lexeme.state.Match(
|
|
||||||
Int: () => Ast.Expr.Int(Int32.Parse(lexeme.lexeme)).Singleton(),
|
|
||||||
String: () => Ast.Expr.String(lexeme.lexeme).Singleton(),
|
|
||||||
Ident: () => Enumerable.Empty<Ast.Expr>(), // TODO
|
|
||||||
And: () => Enumerable.Empty<Ast.Expr>(), // TODO
|
|
||||||
Plus: () => Enumerable.Empty<Ast.Expr>(), // TODO
|
|
||||||
Times: () => Enumerable.Empty<Ast.Expr>(), // TODO
|
|
||||||
Space: () => Enumerable.Empty<Ast.Expr>(), // ignore
|
|
||||||
Eq: () => Enumerable.Empty<Ast.Expr>(), // TODO
|
|
||||||
End: () => Enumerable.Empty<Ast.Expr>(), // TODO
|
|
||||||
Decimal: () => Enumerable.Empty<Ast.Expr>(), // TODO
|
|
||||||
StringOpen: () => Enumerable.Empty<Ast.Expr>(), // TODO
|
|
||||||
StringClose: () => Enumerable.Empty<Ast.Expr>()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.Single()
|
|
||||||
.ElseThrow(() => new ParserErrorException(
|
|
||||||
"empty file or more than one expression in file."));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RecursiveDescent(IEnumerable<Lexeme> e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notes:
|
// Notes:
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
42
|
|
@ -1 +1 @@
|
||||||
40 + 2 == 40 + 1 + 1 && true
|
1 == 1 && true
|
4
main.cs
4
main.cs
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using SearchOption = System.IO.SearchOption;
|
using SearchOption = System.IO.SearchOption;
|
||||||
using Compiler = System.Func<Ast.Expr, string>;
|
using Compiler = System.Func<Ast.AstNode, string>;
|
||||||
using static Global;
|
using static Global;
|
||||||
|
|
||||||
public static class MainClass {
|
public static class MainClass {
|
||||||
|
@ -47,7 +47,7 @@ public static class MainClass {
|
||||||
Console.WriteLine($"\x1b[1;33m{source}: expected {expectedStr} but got {actualStr}.\x1b[m\n");
|
Console.WriteLine($"\x1b[1;33m{source}: expected {expectedStr} but got {actualStr}.\x1b[m\n");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
Console.Write("\x1b[1;32mOK\x1b[m"); // \r at the end for quiet
|
Console.Write("\x1b[1;32mOK\x1b[m\n"); // \r at the end for quiet
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user