笔记 - 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-灵活的构建
一.灵活构建的意义 一个优秀的构建系统必须足够灵活,它应该能够让项目在不同的环境下都能成功地构建.例如,典型的项目都会有开发环境.测试环境和产品环境,这些环境的数据库配置不尽相同,那么项目构建的时候就 ...
随机推荐
- React 高阶组件浅析
高阶组件的这种写法的诞生来自于社区的实践,目的是解决一些交叉问题(Cross-Cutting Concerns).而最早时候 React 官方给出的解决方案是使用 mixin .而 React 也在官 ...
- pycharm 中文乱码
新机的pycharm首次输出中文竟然在控制台报错了,着实让我头疼了一下午 我用的PyCharm是2018.3版本 在调用os.system()的过程中遇到了控制台中文乱码的问题,具体如下 找到对应 ...
- 2013年各大小IT公司待遇,绝对真实,一线数据!(初版)
本人西电硕士,根据今年找工作的情况以及身边同学的汇总,总结各大公司的待遇如下,吐血奉献给各位学弟学妹,公司比较全,你想去的公司不在这里面,基本上是无名小公司了:无名小公司有时也很给力哦以下绝对是各大公 ...
- osg 3ds模型加载与操作
QString item1 = QString::fromStdString(groupParam->getChild(k)->getName()); QStandardItem* ite ...
- 最简易的python web框架的后端实现
1.源代码 #!/usr/bin/python #encoding=utf-8 from flask import json, Flask, request app = Flask(__name__) ...
- Qt编写自定义控件37-发光按钮(会呼吸的痛)
一.前言 这个控件是好早以前写的,已经授权过好几个人开源过此控件代码,比如红磨坊小胖,此控件并不是来源于真实需求,而仅仅是突发奇想,类似于星星的闪烁,越到边缘越来越淡,定时器动态改变边缘发光的亮度,产 ...
- hadoop目录命令
下面是经常使用到的,以此记录备忘 1.查看hadoop目录 命令: hadoop fs -ls / 2.创建目录 命令:hadoop fs -mkdir /目录名 3.将文件上传hadoop中(也就是 ...
- python中os.popen, os.system()区别
直接上个例子吧,注意结果,os.system的结果只是命令执行结果的返回值,执行成功为0: >>> a=os.system('ls') Applications Movies pyt ...
- PAT 甲级 1045 Favorite Color Stripe (30 分)(思维dp,最长有序子序列)
1045 Favorite Color Stripe (30 分) Eva is trying to make her own color stripe out of a given one. S ...
- Paper Mark2
论文:CBAM: Convolutional Block Attention Module 论文链接 pytorch代码 论文:Approach for Fashion Style Recogniti ...