Parse a subset of expressions

This commit is contained in:
Brandon Dyck 2023-06-28 12:12:14 -06:00
parent b9b8c8e24b
commit 77b1fab74f
3 changed files with 194 additions and 6 deletions

View File

@ -6,7 +6,7 @@ namespace Finn;
class ASTPrinter : IVisitor<string>
{
string print(Expr expr)
public string print(Expr expr)
{
return expr.accept(this);
}

173
Parser.cs Normal file
View File

@ -0,0 +1,173 @@
namespace Finn;
using Finn.AST;
using System;
using System.Collections.Generic;
class Parser
{
private class ParseError : Exception { }
private readonly List<Token> tokens;
private int current = 0;
public Parser(List<Token> tokens)
{
this.tokens = tokens;
}
public Expr? parse()
{
try
{
return expression();
}
catch (ParseError)
{
return null;
}
}
// Parsing primitives
private bool match(params TokenType[] types)
{
foreach (TokenType type in types)
{
if (check(type))
{
advance();
return true;
}
}
return false;
}
private bool check(TokenType type)
{
if (isAtEnd()) return false;
return peek().type == type;
}
private Token advance()
{
if (!isAtEnd()) current++;
return previous();
}
private bool isAtEnd()
{
return peek().type == TokenType.EOF;
}
private Token peek()
{
return tokens[current];
}
private Token previous()
{
return tokens[current - 1];
}
private Token consume(TokenType type, string message)
{
if (check(type)) return advance();
throw error(peek(), message);
}
private ParseError error(Token token, string message)
{
Program.error(token, message);
return new ParseError();
}
private void synchronize()
{
advance();
while (!isAtEnd())
{
switch (peek().type)
{
case TokenType.Def:
case TokenType.Type:
return;
}
advance();
}
}
// Helpers
private Expr binaryLeft(Func<Expr> next, params TokenType[] types)
{
Expr expr = next();
while (match(types))
{
Token op = previous();
Expr right = next();
expr = new Binary { Left = expr, Op = op, Right = right };
}
return expr;
}
// Rules
private Expr expression()
{
return equality();
}
private Expr equality()
{
Expr expr = comparison();
while (match(TokenType.BangEqual, TokenType.DoubleEqual))
{
Token op = previous();
Expr right = comparison();
expr = new Binary { Left = expr, Op = op, Right = right };
}
return expr;
}
private Expr comparison() =>
binaryLeft(sum, TokenType.Greater, TokenType.GreaterEqual, TokenType.Less, TokenType.LessEqual);
private Expr sum() => binaryLeft(product, TokenType.Minus, TokenType.Plus);
private Expr product() => binaryLeft(unary, TokenType.Slash, TokenType.Asterisk);
private Expr unary()
{
if (match(TokenType.Bang, TokenType.Minus))
{
Token op = previous();
Expr right = unary();
return new Unary { Op = op, Right = right };
}
return primary();
}
private Expr primary()
{
if (match(TokenType.Number, TokenType.String))
{
return new Literal { Value = previous().literal };
}
if (match(TokenType.LParen))
{
Expr expr = expression();
consume(TokenType.RParen, "Expect ')' after expression.");
return new Grouping { Expression = expr };
}
throw error(peek(), "Expect expression.");
}
}

View File

@ -1,7 +1,7 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using Finn.AST;
namespace Finn;
@ -36,11 +36,12 @@ class Program
{
var scanner = new Scanner(src);
List<Token> tokens = scanner.scanTokens();
Parser parser = new Parser(tokens);
Expr? expression = parser.parse();
foreach (Token token in tokens)
{
Console.WriteLine(token);
}
if (hadError || expression == null) return;
Console.WriteLine(new ASTPrinter().print(expression));
}
static void runPrompt()
@ -62,6 +63,18 @@ class Program
report(line, "", message);
}
public static void error(Token token, String message)
{
if (token.type == TokenType.EOF)
{
report(token.line, " at end", message);
}
else
{
report(token.line, " at '" + token.lexeme + "'", message);
}
}
static void report(int line, String where, String message)
{
Console.WriteLine($"[line {line}] Error{where}: {message}");
@ -102,6 +115,7 @@ public enum TokenType
Type,
Alias,
Recurse,
Def,
Blank,
Identifier, Number, String,
EOF
@ -119,6 +133,7 @@ class Scanner
private static readonly Dictionary<String, TokenType> keywords = new Dictionary<string, TokenType>()
{
{"def", TokenType.Def},
{"when", TokenType.When},
{"is", TokenType.Is},
{"let", TokenType.Let},