笔记 - C#从头开始构建编译器 - 3
视频与PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-03.md
作者是 Immo Landwerth(https://twitter.com/terrajobst),微软 .NET 团队的项目经理。
这一集前半段主要是重构代码,后半段的主要内容:
1. 变量与赋值表达式
2. 加强诊断信息
Parser 非常清晰
using System.Collections.Generic; namespace Minsk.CodeAnalysis.Syntax
{
internal sealed class Parser
{
private readonly SyntaxToken[] _tokens;
private int _position;
private DiagnosticBag _diagnostics = new DiagnosticBag(); public Parser(string text)
{
var tokens = new List<SyntaxToken>(); var lexer = new Lexer(text);
SyntaxToken token;
do
{
token = lexer.Lex();
if (token.Kind != SyntaxKind.WhiteSpaceToken && token.Kind != SyntaxKind.BadToken)
tokens.Add(token);
} while (token.Kind != SyntaxKind.EndOfFileToken); _tokens = tokens.ToArray();
_diagnostics.AddRange(lexer.Diagnostics);
} public DiagnosticBag Diagnostics => _diagnostics; private SyntaxToken Peek(int offset)
{
var index = _position + offset;
if (index >= _tokens.Length)
return _tokens[_tokens.Length - 1];
return _tokens[index];
} private SyntaxToken Current => Peek(0); private SyntaxToken NextToken()
{
var token = Current;
_position++;
return token;
} private SyntaxToken MatchToken(SyntaxKind kind)
{
if (Current.Kind == kind)
return NextToken(); _diagnostics.ReportUnexpectedToken(Current.Span, Current.Kind, kind);
return new SyntaxToken(kind, Current.Position, null, null);
} public SyntaxTree Parse()
{
var expression = ParseExpression();
var endOfFileToken = MatchToken(SyntaxKind.EndOfFileToken);
return new SyntaxTree(_diagnostics, expression, endOfFileToken);
} private ExpressionSyntax ParseExpression()
{
return ParseAssignmentExpression();
} private ExpressionSyntax ParseAssignmentExpression()
{
if (Peek(0).Kind == SyntaxKind.IdentifierToken && Peek(1).Kind == SyntaxKind.EqualsToken)
{
var identifierToken = NextToken();
var equalsToken = NextToken();
var right = ParseAssignmentExpression();
return new AssignmentExpressionSyntax(identifierToken, equalsToken, right);
} return ParseBinaryExpression();
} private ExpressionSyntax ParseBinaryExpression(int parentPrecedence = 0)
{
ExpressionSyntax left;
var unaryOperatorPrecedence = Current.Kind.GetUnaryOperatorPrecedence();
if (unaryOperatorPrecedence != 0 && unaryOperatorPrecedence >= parentPrecedence)
{
var operatorToken = NextToken();
var operand = ParseBinaryExpression(unaryOperatorPrecedence);
left = new UnaryExpressionSyntax(operatorToken, operand);
}
else
left = ParsePrimaryExpression(); while (true)
{
var precedence = Current.Kind.GetBinaryOperatorPrecedence();
if (precedence == 0 || precedence <= parentPrecedence)
break; var operatorToken = NextToken();
var right = ParseBinaryExpression(precedence);
left = new BinaryExpressionSyntax(left, operatorToken, right);
} return left;
} private ExpressionSyntax ParsePrimaryExpression()
{
switch (Current.Kind)
{
case SyntaxKind.OpenParenthesisToken:
{
var left = NextToken();
var expression = ParseExpression();
var right = MatchToken(SyntaxKind.CloseParenthesisToken);
return new ParenthesizedExpressionSyntax(left, expression, right);
} case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
{
var keywordToken = NextToken();
var value = keywordToken.Kind == SyntaxKind.TrueKeyword;
return new LiteralExpressionSyntax(keywordToken, value);
} case SyntaxKind.IdentifierToken:
{
var identifierToken = NextToken();
return new NameExpressionSyntax(identifierToken);
} default:
{
var numberToken = MatchToken(SyntaxKind.NumberToken);
return new LiteralExpressionSyntax(numberToken);
}
} }
}
}
作为语义分析的 Binder 也非常清晰
using System;
using System.Collections.Generic;
using System.Linq;
using Minsk.CodeAnalysis.Syntax; namespace Minsk.CodeAnalysis.Binding
{
internal sealed class Binder
{
private readonly DiagnosticBag _diagnostics = new DiagnosticBag ();
private readonly Dictionary<VariableSymbol, object> _variables; public Binder(Dictionary<VariableSymbol, object> variables)
{
_variables = variables;
} public DiagnosticBag Diagnostics => _diagnostics; public BoundExpression BindExpression(ExpressionSyntax syntax)
{
switch (syntax.Kind)
{
case SyntaxKind.ParenthesizedExpression:
return BindParenthesizedExpression((ParenthesizedExpressionSyntax)syntax);
case SyntaxKind.LiteralExpression:
return BindLiteralExpression((LiteralExpressionSyntax)syntax);
case SyntaxKind.NameExpression:
return BindNameExpression((NameExpressionSyntax)syntax);
case SyntaxKind.AssignmentExpression:
return BindAssignmentExpression((AssignmentExpressionSyntax)syntax);
case SyntaxKind.UnaryExpression:
return BindUnaryExpression((UnaryExpressionSyntax)syntax);
case SyntaxKind.BinaryExpression:
return BindBinaryExpression((BinaryExpressionSyntax)syntax);
default:
throw new Exception($"Unexpected syntax {syntax.Kind}");
}
} private BoundExpression BindParenthesizedExpression(ParenthesizedExpressionSyntax syntax)
{
return BindExpression(syntax.Expression);
} private BoundExpression BindLiteralExpression(LiteralExpressionSyntax syntax)
{
var value = syntax.Value ?? 0;
return new BoundLiteralExpression(value);
} private BoundExpression BindNameExpression(NameExpressionSyntax syntax)
{
var name = syntax.IdentifierToken.Text;
var variable = _variables.Keys.FirstOrDefault(v => v.Name == name);
if (variable == null)
{
_diagnostics.ReportUndefinedName(syntax.IdentifierToken.Span, name);
return new BoundLiteralExpression(0);
} return new BoundVariableExpression(variable);
} private BoundExpression BindAssignmentExpression(AssignmentExpressionSyntax syntax)
{
var name = syntax.IdentifierToken.Text;
var boundExpression = BindExpression(syntax.Expression); var existingVariable = _variables.Keys.FirstOrDefault(v => v.Name == name);
if (existingVariable != null)
_variables.Remove(existingVariable); var variable = new VariableSymbol(name, boundExpression.Type);
_variables[variable] = null; return new BoundAssignmentExpression(variable, boundExpression);
} private BoundExpression BindUnaryExpression(UnaryExpressionSyntax syntax)
{
var boundOperand = BindExpression(syntax.Operand);
var boundOperator = BoundUnaryOperator.Bind(syntax.OperatorToken.Kind, boundOperand.Type);
if (boundOperator == null)
{
_diagnostics.ReportUndefinedUnaryOperator(syntax.OperatorToken.Span, syntax.OperatorToken.Text, boundOperand.Type);
return boundOperand;
}
return new BoundUnaryExpression(boundOperator, boundOperand);
} private BoundExpression BindBinaryExpression(BinaryExpressionSyntax syntax)
{
var boundLeft = BindExpression(syntax.Left);
var boundRight = BindExpression(syntax.Right);
var boundOperator = BoundBinaryOperator.Bind(syntax.OperatorToken.Kind, boundLeft.Type, boundRight.Type);
if (boundOperator == null)
{
_diagnostics.ReportUndefinedBinaryOperator(syntax.OperatorToken.Span, syntax.OperatorToken.Text, boundLeft.Type, boundRight.Type);
return boundLeft;
}
return new BoundBinaryExpression(boundLeft, boundOperator, boundRight);
}
}
}
C#语言点:
public static class Enumerable
{
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
}
FirstOrDefault 可以使用谓词作为判断条件,Binder 的 55 行使用了 Lambda 表达式。
笔记 - C#从头开始构建编译器 - 3的更多相关文章
- 笔记 - C#从头开始构建编译器 - 2
视频与PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-02.md 作者是 Immo Landwerth(https:// ...
- 笔记 - C#从头开始构建编译器 - 1
视频与PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-01.md 作者是 Immo Landwerth(https:// ...
- keras 学习笔记:从头开始构建网络处理 mnist
全文参考 < 基于 python 的深度学习实战> import numpy as np from keras.datasets import mnist from keras.model ...
- 软工读书笔记 week 9 ——《构建之法》
软工读书笔记 week 9 ——<构建之法> 最近的三周我们正式开始我们的项目.然后我也把<构建之法>中的相关章节再拿出来读了一番.以下是一些 ...
- [HeadFrist-HTMLCSS学习笔记]第三章构建模块:Web页面建设
[HeadFrist-HTMLCSS学习笔记]第三章构建模块:Web页面建设 敲黑板!! <q>元素添加短引用,<blockquote>添加长引用 在段落里添加引用就使用< ...
- blfs(systemd版本)学习笔记-为桌面环境构建xorg服务
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! lfs准备使用桌面环境,首先需要构建xorg服务 xorg服务项目地址:http://www.linuxfromscratch. ...
- blfs(systemv版本)学习笔记-为桌面环境构建xorg服务
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! lfs准备使用桌面环境,首先需要构建xorg服务 xorg服务项目地址:http://www.linuxfromscratch. ...
- 软工读书笔记 week 5 ——《构建之法》
本周主要对<构建之法>中的一部分进行阅读. 一.软件与软件工程究竟是什么? 本书的概论部分就指出“软件 = 程序 + 软件工程”.而我们这门课的名字就叫“现代软件工程”.其实在上课之前,我 ...
- 《Maven实战》笔记-10-灵活的构建
一.灵活构建的意义 一个优秀的构建系统必须足够灵活,它应该能够让项目在不同的环境下都能成功地构建.例如,典型的项目都会有开发环境.测试环境和产品环境,这些环境的数据库配置不尽相同,那么项目构建的时候就 ...
随机推荐
- [java]三种自定义链表排序方式
代码: import java.util.ArrayList; import java.util.Comparator; import java.util.List; class Emp{ Strin ...
- 连接池大小调优 原创: ImportNew ImportNew 2017-06-07
连接池大小调优 原创: ImportNew ImportNew 2017-06-07
- Swift_IOS之UIActivityIndicatorView加载齿轮控件
// // ViewController.swift // helloIOS // // Created by loaderman on 2019/1/25. // Copyright © 2019年 ...
- iOS8.0如何使用Touch ID来做验证
对于Objective-C而言,只要几行代码即可搞定. 比如: #import <LocalAuthentication/LocalAuthentication.h> - (void)vi ...
- 使用java自带线程池
java提供自带的线程池,而不需要自己去开发一个自定义线程池了. 线程池类ThreadPoolExecutor在包java.util.concurrent下 ThreadPoolExecutor ...
- 【408】C函数中的ADT
类似类的形式 boardADT.h:所有的宏.声明等部分写在这里 boardADT.c:只需要 #inclue "boardADT.h",不需要 include 其他系统头文件,然 ...
- mybatis/tk mybatis下实体字段是关键字/保留字,执行报错
实体如下: import com.fasterxml.jackson.annotation.JsonFormat; import com.xxx.web.bean.PagesStatic; impor ...
- 【AI教育】可以看看行业痛点分析
http://www.woshipm.com/it/2801582.html 至于解决方案嘛,还在堆砌技术的阶段.
- DB2函数简单示例
DB2中的函数原理同其他编程语言中的函数,均为输入几个参数,同时返回一个值. 下面的例子演示一个寻找某一次考试中成绩最好的学生的姓名. 首先,我们新建一个表SCORE用于表示考试,并插入几条数据: D ...
- Django:django后台传递数据到js中
#奇怪传递字典时前台接收不到???datalist= [{'site': '自强学堂', 'author': '涂伟忠'}]#只要列表能接收# datalist=[[93, 93, 0, 100.01 ...