视频与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的更多相关文章

  1. 笔记 - C#从头开始构建编译器 - 2

    视频与PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-02.md 作者是 Immo Landwerth(https:// ...

  2. 笔记 - C#从头开始构建编译器 - 1

    视频与PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-01.md 作者是 Immo Landwerth(https:// ...

  3. keras 学习笔记:从头开始构建网络处理 mnist

    全文参考 < 基于 python 的深度学习实战> import numpy as np from keras.datasets import mnist from keras.model ...

  4. 软工读书笔记 week 9 ——《构建之法》

    软工读书笔记  week 9                 ——<构建之法> 最近的三周我们正式开始我们的项目.然后我也把<构建之法>中的相关章节再拿出来读了一番.以下是一些 ...

  5. [HeadFrist-HTMLCSS学习笔记]第三章构建模块:Web页面建设

    [HeadFrist-HTMLCSS学习笔记]第三章构建模块:Web页面建设 敲黑板!! <q>元素添加短引用,<blockquote>添加长引用 在段落里添加引用就使用< ...

  6. blfs(systemd版本)学习笔记-为桌面环境构建xorg服务

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! lfs准备使用桌面环境,首先需要构建xorg服务 xorg服务项目地址:http://www.linuxfromscratch. ...

  7. blfs(systemv版本)学习笔记-为桌面环境构建xorg服务

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! lfs准备使用桌面环境,首先需要构建xorg服务 xorg服务项目地址:http://www.linuxfromscratch. ...

  8. 软工读书笔记 week 5 ——《构建之法》

    本周主要对<构建之法>中的一部分进行阅读. 一.软件与软件工程究竟是什么? 本书的概论部分就指出“软件 = 程序 + 软件工程”.而我们这门课的名字就叫“现代软件工程”.其实在上课之前,我 ...

  9. 《Maven实战》笔记-10-灵活的构建

    一.灵活构建的意义 一个优秀的构建系统必须足够灵活,它应该能够让项目在不同的环境下都能成功地构建.例如,典型的项目都会有开发环境.测试环境和产品环境,这些环境的数据库配置不尽相同,那么项目构建的时候就 ...

随机推荐

  1. 数据库Sequence创建与使用

    最近几天使用Oracle的sequence序列号,发现对如何创建.修改.使用存在很多迷茫点,在上网寻找答案后,根据各路大神的总结,汇总下对自己的学习成果: 在Oracle中sequence就是序号,每 ...

  2. 【Oracle/Java】以Insert ALL方式向表中插入百万条记录,耗时9分17秒

    由于按一千条一插程序长期无反应,之后改为百条一插方式,运行完发现插入百万记录需要9m17s,虽然比MySQL效率差,但比单条插入已经好不少了. 对Oracle的批量插入语法不明的请参考:https:/ ...

  3. PyMouse、PyKeyboard用python操作鼠标和键盘

    1.PyUserInput 简介 PyUserInput是一个使用python的跨平台的操作鼠标和键盘的模块,非常方便使用.支持的平台及依赖如下: Linux - Xlib Mac - Quartz, ...

  4. python 设计模式之中介者模式

    #先啰嗦一下 至少半个多月的样子没写博客了,月初去了趟黄山,赏了美景,自然没时间也没条件敲博客了,一个多星期就这么过去了.返回深圳后,工作积压了一堆,然后白天就马不停蹄的忙工作,晚上回家伺候小娃,又想 ...

  5. 为什么vue-cli创建的build文件下没有dev-server.js文件

    在新版本的Vue开发中,通过vue-cli创建的build文件夹下面已经没有了旧版本中的dev-server.js文件新版本的vue已将dev-server.js与webpack.dev.conf.j ...

  6. [转][C#]dll 引用

    本文转自:https://zhidao.baidu.com/question/1176198151354174139.html 首先,对应关系: C++ C#===================== ...

  7. KDChart example

    /******************************************************************************** ** Form generated ...

  8. 19 Flutter 自定义AppBar 定义顶部Tab切换 底部Tab结合顶部Tab实现类似头条页面布局(27分36秒)

    Flutter AppBar自定义顶部导航按钮图标.颜色以及TabBar定义顶部Tab切换. leading:在标题前面显示的一个控件,在首页通常显示应用的logo:在其他界面通常显示为付汇按钮. t ...

  9. java通过telnet远程至windows机器执行dos命令

    准备工作,远程windows机器中开启telnet服务,将远程登录用户添加至telnetClients用户组 核心代码: import java.io.IOException; import java ...

  10. 【AI教育】可以看看行业痛点分析

    http://www.woshipm.com/it/2801582.html 至于解决方案嘛,还在堆砌技术的阶段.