笔记 - 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-灵活的构建
一.灵活构建的意义 一个优秀的构建系统必须足够灵活,它应该能够让项目在不同的环境下都能成功地构建.例如,典型的项目都会有开发环境.测试环境和产品环境,这些环境的数据库配置不尽相同,那么项目构建的时候就 ...
随机推荐
- vue使用install函数把组件做成插件方便全局调用
在vue项目中,我们可以自定义组件,像element-ui一样使用Vue.use()方法来使用,具体实现方法: 1.首先新建一个Cmponent.vue文件 // Cmponent.vue <t ...
- Faster RCNN论文学习
Faster R-CNN在Fast R-CNN的基础上的改进就是不再使用选择性搜索方法来提取框,效率慢,而是使用RPN网络来取代选择性搜索方法,不仅提高了速度,精确度也更高了 Faster R-CNN ...
- log4j:ERROR setFile(null,true) call failed.错误解决
首先说明,我是用hive执行bin/hiveserver2时出现的这个错误.如下图所示,红框中的内容也清晰的告诉我们出错的原因和文件路径. 之后,我查看了一下该路径.发现我用的是beifeng的用户, ...
- ALV显示金额字段值扩大100倍
内表数据 物料 库位 期末庫存金額F0D7004DSA 1PYF 701410.944F0D7004DSA 1SNT ...
- activity 工作流
https://blog.csdn.net/cs_hnu_scw/article/details/79059965 https://blog.csdn.net/xnf1991/article/deta ...
- ansible软件部署
每一个软件或集群都创建一个单独的角色. mkdir -pv ansible/roles/{jdk,hadoop,spark}/{tasks,files,templates,meta,handlers, ...
- vmware安装centos7.5、配置网卡、环境配置
1.vmware安装centos7.5虚拟机 参考连接: https://blog.csdn.net/guo_ridgepole/article/details/78973763 可能遇到的问题 ...
- easyui-datagrid配置宽度高度自适应
在style属性中,去除之前添加的width和height属性(如果有的话),然后添加"fit:false"即可.
- 【ARTS】01_36_左耳听风-201900715~201900721
ARTS: Algrothm: leetcode算法题目 Review: 阅读并且点评一篇英文技术文章 Tip/Techni: 学习一个技术技巧 Share: 分享一篇有观点和思考的技术文章 Algo ...
- django model的update时auto_now不被更新的原因
gmt_create自动添加auto_now_add:gmt_modify自动更新auto_now class CommonInfo(models.Model): """ ...