Make it possible to nest match-expressions
Although this fixes #46, this introduces a new bug for expressions that require punctuation to be turned off. We will adress this in a separate issue.
This commit is contained in:
parent
8addcb0990
commit
ac7467bccb
5 changed files with 102 additions and 31 deletions
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
@ -9,7 +9,7 @@
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Debug",
|
"name": "Debug",
|
||||||
"program": "${workspaceFolder}/build/bolt",
|
"program": "${workspaceFolder}/build/bolt",
|
||||||
"args": [ "--direct-diagnostics", "verify", "test/checker/wrong_return_type.bolt" ],
|
"args": [ "--direct-diagnostics", "check", "test.bolt" ],
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"preLaunchTask": "CMake: build"
|
"preLaunchTask": "CMake: build"
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,9 @@ namespace bolt {
|
||||||
void checkLineFoldEnd();
|
void checkLineFoldEnd();
|
||||||
void skipToLineFoldEnd();
|
void skipToLineFoldEnd();
|
||||||
|
|
||||||
|
void disablePunctuation();
|
||||||
|
void enablePunctuation();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Parser(TextFile& File, Stream<Token*>& S, DiagnosticEngine& DE);
|
Parser(TextFile& File, Stream<Token*>& S, DiagnosticEngine& DE);
|
||||||
|
|
|
@ -61,16 +61,25 @@ namespace bolt {
|
||||||
enum class FrameType {
|
enum class FrameType {
|
||||||
Block,
|
Block,
|
||||||
LineFold,
|
LineFold,
|
||||||
Fallthrough,
|
};
|
||||||
|
|
||||||
|
struct Frame {
|
||||||
|
FrameType Type;
|
||||||
|
int Parens = 0;
|
||||||
|
int Braces = 0;
|
||||||
|
int Brackets = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Punctuator : public BufferedStream<Token*> {
|
class Punctuator : public BufferedStream<Token*> {
|
||||||
|
|
||||||
Stream<Token*>& Tokens;
|
Stream<Token*>& Tokens;
|
||||||
|
|
||||||
std::stack<FrameType> Frames;
|
std::stack<Frame> Frames;
|
||||||
std::stack<TextLoc> Locations;
|
std::stack<TextLoc> Locations;
|
||||||
|
|
||||||
|
bool ShouldYieldNextTokenInLineFold = false;
|
||||||
|
bool isTerminal(NodeKind Kind);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual Token* read() override;
|
virtual Token* read() override;
|
||||||
|
|
|
@ -492,8 +492,8 @@ after_tuple_element:
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchExpression* Parser::parseMatchExpression() {
|
MatchExpression* Parser::parseMatchExpression() {
|
||||||
auto T0 = expectToken<MatchKeyword>();
|
auto Match = expectToken<MatchKeyword>();
|
||||||
if (!T0) {
|
if (!Match) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto T1 = Tokens.peek();
|
auto T1 = Tokens.peek();
|
||||||
|
@ -506,12 +506,12 @@ after_tuple_element:
|
||||||
} else {
|
} else {
|
||||||
Value = parseExpression();
|
Value = parseExpression();
|
||||||
if (!Value) {
|
if (!Value) {
|
||||||
T0->unref();
|
Match->unref();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
BlockStart = expectToken<class BlockStart>();
|
BlockStart = expectToken<class BlockStart>();
|
||||||
if (!BlockStart) {
|
if (!BlockStart) {
|
||||||
T0->unref();
|
Match->unref();
|
||||||
Value->unref();
|
Value->unref();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -544,7 +544,7 @@ after_tuple_element:
|
||||||
checkLineFoldEnd();
|
checkLineFoldEnd();
|
||||||
Cases.push_back(new MatchCase { Pattern, RArrowAlt, Expression });
|
Cases.push_back(new MatchCase { Pattern, RArrowAlt, Expression });
|
||||||
}
|
}
|
||||||
return new MatchExpression(static_cast<MatchKeyword*>(T0), Value, BlockStart, Cases);
|
return new MatchExpression(Match, Value, BlockStart, Cases);
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordExpression* Parser::parseRecordExpression() {
|
RecordExpression* Parser::parseRecordExpression() {
|
||||||
|
@ -688,10 +688,13 @@ after_tuple_element:
|
||||||
case NodeKind::LineFoldEnd:
|
case NodeKind::LineFoldEnd:
|
||||||
case NodeKind::BlockStart:
|
case NodeKind::BlockStart:
|
||||||
case NodeKind::EndOfFile:
|
case NodeKind::EndOfFile:
|
||||||
// Can recover from this one
|
|
||||||
RParen = nullptr;
|
|
||||||
DE.add<UnexpectedTokenDiagnostic>(File, T2, std::vector { NodeKind::RParen, NodeKind::Comma });
|
DE.add<UnexpectedTokenDiagnostic>(File, T2, std::vector { NodeKind::RParen, NodeKind::Comma });
|
||||||
goto after_tuple_elements;
|
LParen->unref();
|
||||||
|
for (auto [E, Comma]: Elements) {
|
||||||
|
E->unref();
|
||||||
|
Comma->unref();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
after_tuple_elements:
|
after_tuple_elements:
|
||||||
|
@ -764,6 +767,7 @@ finish:
|
||||||
if (T1->getKind() == NodeKind::LineFoldEnd
|
if (T1->getKind() == NodeKind::LineFoldEnd
|
||||||
|| T1->getKind() == NodeKind::RParen
|
|| T1->getKind() == NodeKind::RParen
|
||||||
|| T1->getKind() == NodeKind::RBrace
|
|| T1->getKind() == NodeKind::RBrace
|
||||||
|
|| T1->getKind() == NodeKind::RBracket
|
||||||
|| T1->getKind() == NodeKind::BlockStart
|
|| T1->getKind() == NodeKind::BlockStart
|
||||||
|| T1->getKind() == NodeKind::Comma
|
|| T1->getKind() == NodeKind::Comma
|
||||||
|| ExprOperators.isInfix(T1)) {
|
|| ExprOperators.isInfix(T1)) {
|
||||||
|
|
|
@ -426,17 +426,77 @@ after_string_contents:
|
||||||
|
|
||||||
Punctuator::Punctuator(Stream<Token*>& Tokens):
|
Punctuator::Punctuator(Stream<Token*>& Tokens):
|
||||||
Tokens(Tokens) {
|
Tokens(Tokens) {
|
||||||
Frames.push(FrameType::Block);
|
Frames.push({ FrameType::Block });
|
||||||
Locations.push(TextLoc { 0, 0 });
|
Locations.push(TextLoc { 0, 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isCloseDelim(NodeKind Kind) {
|
||||||
|
switch (Kind) {
|
||||||
|
case NodeKind::RParen:
|
||||||
|
case NodeKind::RBrace:
|
||||||
|
case NodeKind::RBracket:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct DelimCounter {
|
||||||
|
// int Parens = 0;
|
||||||
|
// int Braces = 0;
|
||||||
|
// int Brackets = 0;
|
||||||
|
// };
|
||||||
|
|
||||||
|
bool Punctuator::isTerminal(NodeKind Kind) {
|
||||||
|
auto& Frame = Frames.top();
|
||||||
|
switch (Kind) {
|
||||||
|
case NodeKind::RParen:
|
||||||
|
return Frame.Parens == 0;
|
||||||
|
case NodeKind::RBrace:
|
||||||
|
return Frame.Braces == 0;
|
||||||
|
case NodeKind::RBracket:
|
||||||
|
return Frame.Brackets == 0;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Token* Punctuator::read() {
|
Token* Punctuator::read() {
|
||||||
|
|
||||||
auto T0 = Tokens.peek();
|
auto T0 = Tokens.peek();
|
||||||
|
auto& RefLoc = Locations.top();
|
||||||
|
auto& Frame = Frames.top();
|
||||||
|
|
||||||
switch (T0->getKind()) {
|
switch (T0->getKind()) {
|
||||||
|
case NodeKind::LParen:
|
||||||
|
++Frame.Parens;
|
||||||
|
break;
|
||||||
case NodeKind::LBrace:
|
case NodeKind::LBrace:
|
||||||
Frames.push(FrameType::Fallthrough);
|
++Frame.Braces;
|
||||||
|
break;
|
||||||
|
case NodeKind::LBracket:
|
||||||
|
++Frame.Brackets;
|
||||||
|
break;
|
||||||
|
case NodeKind::RParen:
|
||||||
|
if (Frame.Parens == 0) {
|
||||||
|
// TODO add diagnostic?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--Frame.Parens;
|
||||||
|
break;
|
||||||
|
case NodeKind::RBrace:
|
||||||
|
if (Frame.Braces == 0) {
|
||||||
|
// TODO add diagnostic?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--Frame.Braces;
|
||||||
|
break;
|
||||||
|
case NodeKind::RBracket:
|
||||||
|
if (Frame.Brackets == 0) {
|
||||||
|
// TODO add diagnostic?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--Frame.Brackets;
|
||||||
break;
|
break;
|
||||||
case NodeKind::EndOfFile:
|
case NodeKind::EndOfFile:
|
||||||
{
|
{
|
||||||
|
@ -445,9 +505,7 @@ after_string_contents:
|
||||||
}
|
}
|
||||||
auto Frame = Frames.top();
|
auto Frame = Frames.top();
|
||||||
Frames.pop();
|
Frames.pop();
|
||||||
switch (Frame) {
|
switch (Frame.Type) {
|
||||||
case FrameType::Fallthrough:
|
|
||||||
break;
|
|
||||||
case FrameType::Block:
|
case FrameType::Block:
|
||||||
return new BlockEnd(T0->getStartLoc());
|
return new BlockEnd(T0->getStartLoc());
|
||||||
case FrameType::LineFold:
|
case FrameType::LineFold:
|
||||||
|
@ -458,20 +516,17 @@ after_string_contents:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto RefLoc = Locations.top();
|
switch (Frame.Type) {
|
||||||
switch (Frames.top()) {
|
|
||||||
case FrameType::Fallthrough:
|
|
||||||
{
|
|
||||||
if (T0->getKind() == NodeKind::RBrace) {
|
|
||||||
Frames.pop();
|
|
||||||
}
|
|
||||||
Tokens.get();
|
|
||||||
return T0;
|
|
||||||
}
|
|
||||||
case FrameType::LineFold:
|
case FrameType::LineFold:
|
||||||
{
|
{
|
||||||
if (T0->getStartLine() > RefLoc.Line
|
if (ShouldYieldNextTokenInLineFold) {
|
||||||
&& T0->getStartColumn() <= RefLoc.Column) {
|
ShouldYieldNextTokenInLineFold = false;
|
||||||
|
return Tokens.get();
|
||||||
|
}
|
||||||
|
ShouldYieldNextTokenInLineFold = isTerminal(T0->getKind());
|
||||||
|
if (ShouldYieldNextTokenInLineFold
|
||||||
|
|| (T0->getStartLine() > RefLoc.Line
|
||||||
|
&& T0->getStartColumn() <= RefLoc.Column)) {
|
||||||
Frames.pop();
|
Frames.pop();
|
||||||
Locations.pop();
|
Locations.pop();
|
||||||
return new LineFoldEnd(T0->getStartLoc());
|
return new LineFoldEnd(T0->getStartLoc());
|
||||||
|
@ -480,7 +535,7 @@ after_string_contents:
|
||||||
auto T1 = Tokens.peek(1);
|
auto T1 = Tokens.peek(1);
|
||||||
if (T1->getStartLine() > T0->getEndLine()) {
|
if (T1->getStartLine() > T0->getEndLine()) {
|
||||||
Tokens.get();
|
Tokens.get();
|
||||||
Frames.push(FrameType::Block);
|
Frames.push({ FrameType::Block });
|
||||||
return new BlockStart(T0->getStartLoc());
|
return new BlockStart(T0->getStartLoc());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -488,12 +543,12 @@ after_string_contents:
|
||||||
}
|
}
|
||||||
case FrameType::Block:
|
case FrameType::Block:
|
||||||
{
|
{
|
||||||
if (T0->getStartColumn() <= RefLoc.Column) {
|
if (T0->getStartColumn() <= RefLoc.Column || isTerminal(T0->getKind())) {
|
||||||
Frames.pop();
|
Frames.pop();
|
||||||
return new BlockEnd(T0->getStartLoc());
|
return new BlockEnd(T0->getStartLoc());
|
||||||
}
|
}
|
||||||
|
|
||||||
Frames.push(FrameType::LineFold);
|
Frames.push({ FrameType::LineFold });
|
||||||
Locations.push(T0->getStartLoc());
|
Locations.push(T0->getStartLoc());
|
||||||
|
|
||||||
return Tokens.get();
|
return Tokens.get();
|
||||||
|
|
Loading…
Reference in a new issue