amorphobia / blog Goto Github PK
View Code? Open in Web Editor NEW随便写写
Home Page: https://blog.xuesong.io/
随便写写
Home Page: https://blog.xuesong.io/
搭建 blog 以来几乎没写过什么文字,导致了想要写的时候不知道 Hexo 该怎么用,然后去找官方文档,从繁杂的文档中找到自己想要的。现在就写一篇笔记来记录发新文章需要的命令吧。
$ hexo new <title>
一般情况 <title>
就用单词加短线,在文章里再改成中文标题就好。
$ hexo generate
可以加选项 -d
或者 --deploy
生成后立即部署,上传到 github.
$ hexo server
部署之前看看有什么毛病没有。可以加选项 -s
或者 --static
只使用生成好了的静态文件。
$ hexo deploy
可以加选项 -g
或者 --generate
预先生成静态文件。
我在 Windows 里日常用的是 Windows Terminal 中的 PowerShell。这就有一个问题,一旦创建了配置文件,PowerShell 启动速度就会很慢,即使删除所有配置文件,启动速度也回不到从前。因此在最近一次重装系统后,我再也没搞过配置文件,我的 PowerShell 就一直保持秒开的速度。
可是无配置文件的 PowerShell 的确是少了些方便,今天突发奇想,既然 PowerShell 自己读取配置很慢,那我能不能在启动它之后再指定一个配置文件,这个文件不在 PowerShell 默认的路径里,这样即使以后觉得速度太慢而删除它,也能恢复秒开。
系统默认的配置文件路径分别是
$PSHOME\Profile.ps1
$PSHOME\Microsoft.PowerShell_profile.ps1
$Home\[My ]DocumentsPowerShell\\Profile.ps1
$Home\[My ]DocumentsPowerShell\\Microsoft.PowerShell_profile.ps1
挺好,那我在用户目录下创建一个 .profile.ps1
文件作为我的配置文件,就不会触发 PowerShell 的启动加载了。那么问题就变成了:
中间的尝试略过的话,最后总结了一下解决方法。
由于我对 PowerShell 不熟悉,所以加载这个自定义的配置文件也算一个问题。在类 Unix 系统里加载 Shell 配置我是知道的,直接 source <file>
就成了。我来试试吧:
PS C:\Users\amorphobia> source $HOME\.profile.ps1
source : 无法将“source”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径
正确,然后再试一次。
所在位置 行:1 字符: 1
+ source $HOME\.profile.ps1
+ ~~~~~~
+ CategoryInfo : ObjectNotFound: (source:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
寄了呀!其实不试也猜得到,从来都没听说过 PowerShell 有 source
命令……那试试直接运行呢?
PS C:\Users\amorphobia> $HOME\.profile.ps1
所在位置 行:1 字符: 6
+ $HOME\.profile.ps1
+ ~~~~~~~~~~~~~
表达式或语句中包含意外的标记“\.profile.ps1”。
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
嗯……可能直接运行也不行吧?于是网上搜了一下,发现其实脚本是可以直接运行的,问题出现在这个 $HOME
环境变量,需要将其展开成完整的路径,才可以运行。那如果我不想展开呢?解决方案也有,就是使用 &
操作符。赶快试试吧:
PS C:\Users\amorphobia> & $HOME\.profile.ps1
PS C:\Users\amorphobia>
没有报错,而且配置生效了!撒花🎉🎉🎉
由于我用的是 Windows 终端,能指定 PowerShell 启动时的参数,那么 PowerShell 应该也需要有类似 sh -c <cmd>
这样的命令,这回直接看看帮助吧:
PS C:\Users\amorphobia> powershell -h
PowerShell[.exe] [-PSConsoleFile <文件> | -Version <版本>]
[-NoLogo] [-NoExit] [-Sta] [-Mta] [-NoProfile] [-NonInteractive]
[-InputFormat {Text | XML}] [-OutputFormat {Text | XML}]
[-WindowStyle <样式>] [-EncodedCommand <Base64 编码命令>]
[-ConfigurationName <字符串>]
[-File <文件路径> <参数>] [-ExecutionPolicy <执行策略>]
[-Command { - | <脚本块> [-args <参数数组>]
| <字符串> [<命令参数>] } ]
......
诶,有个 -Command
参数,讲了啥呢?
-Command
执行指定的命令(和任何参数),就好像它们是
在 Windows PowerShell 命令提示符下键入的一样,然后退出,除非
指定了 NoExit。Command 的值可以为 "-"、字符串或
脚本块。
如果 Command 的值为 "-",则从标准输入中读取
命令文本。
如果 Command 的值为脚本块,则脚本块必须
用大括号({})括起来。只有在 Windows PowerShell 中运行 PowerShell.exe 时,
才能指定脚本块。脚本块的结果将作为反序列化的 XML 对象
(而非活动对象)返回到父 Shell。
如果 Command 的值为字符串,则 Command 必须是命令中的
最后一个参数,因为在命令后面键入的所有字符
都将解释为命令参数。
若要编写运行 Windows PowerShell 命令的字符串,请使用以下格式:
"& {<命令>}"
其中,引号表示一个字符串,调用运算符(&)
导致执行命令。
哎呀,帮助里就提示了用 &
来运行命令。好的,加到 Windows 终端的设置里吧!
...
"profiles":
{
"defaults": {},
"list":
[
{
...
"commandline": "powershell.exe -Command \"& $HOME\\.profile.ps1\"",
...
},
...
]
},
...
保存,关掉 Windows 终端,再启动。咦,窗口闪了一下就没了,怎么回事呢?原来还不行,有其他问题呀。
想想 sh -c <cmd>
这个命令,也是运行完成就退出吧,要想运行之后进入交互模式,还得用 sh -ic <cmd>
才行。那么 PowerShell 呢?看看刚才的帮助:
-NoExit
运行启动命令后不退出。
就它了!在 Windows 终端的设置里把这玩意儿加上
...
"profiles":
{
"defaults": {},
"list":
[
{
...
"commandline": "powershell.exe -NoExit -Command \"& $HOME\\.profile.ps1\"",
...
},
...
]
},
...
关掉再试试,成了!
回想一下我最初的需求,万一这东西速度慢了,不想要了,会不会和默认路径的配置文件删除后一样,速度恢复不了?既然我只是在 Windows 终端里干了这事,那就应该不会影响 PowerShell 本身吧。试了试不用 Windows 终端,直接启动 PowerShell 本体,速度依然飞快。嗯,目的达成了。
想达成我的目的只需两步:
C:\Users\amorphobia\.profile.ps1
powershell.exe -NoExit -Command "& $HOME\.profile.ps1"
,如果直接改 Json 配置要注意引号和反斜杠的转义完了。
一直以来,我对于虚岁的理解仅仅是机械地记住了“出生时记为一岁,每过一个春节加一岁”,总觉得这个定义无厘头。近期了解了一些农历,以及古代纪年的知识后,总算是了解了内在的逻辑。
简单来说,虚岁就跟古代纪年的年号是一个道理,甚至可以理解为使用你的名字来作“年号”。当然,在古代,只有皇帝才能有年号,但现代都没有皇帝了,你就意淫自己当个皇帝也无所谓的。
比如你出生后,你爸妈给你起的小名叫“狗蛋”,让你当“狗皇帝”,那么你出生那天所在的农历年就可以叫做“狗蛋元年”,第二年春节开始叫做“狗蛋二年”……以此类推。
这不就是虚岁的记法吗?🤣
以前学习能力还行的时候看不进去这本书,现在脑子转不过来了估计更是学不会了,虽然不知道我了解编译原理有什么作用,姑且当作一个消遣吧。
文章并不是为了完整地写读书笔记,零散地记录一些我不理解的知识,以及看起来比较重要的知识,标题里方括号对应的就是书中的章节,方便我回顾的时候对照看书。
上下文无关文法组成部分:
另外,这里的“终结”不是指目标语言中的语句的终结,而是说这个定义就是它本身了,不再由其他东西产生,比如书里的例2.1
:
list -> list + digit
list -> list - digit
list -> digit
digit -> [0-9] # 这里我直接用了正则来表示数字,我自认为意思是表达到了。
这个文法的终结符号就包括 +
, -
, 0
, 1
, 2
, 3
, 4
, 5
, 6
, 7
, 8
, 9
;而 list
和 digit
是非终结符号。因为它们可以由其他符号产生。
语法分析树 (parse tree) 的性质:
A
是某内部节点的标号,它的子节点的标号依次为 X₁
, X₂
, ... Xₙ
,则必然存在产生式 A -> X₁X₂...Xₙ
。在文法中,运算符按照优先级递增的顺序排列,这里直接给出加减乘除运算符的产生式,如果有更多层优先级,可以类推。
expr -> expr + term | expr - term | term
term -> term * factor | term / factor | factor
factor -> digit | (expr)
A
的节点上,选择一个 A
的产生式,并为这个产生式中的每个符号构造子节点;当前被扫描的终结符号通常称为向前看 (lookahead) 符号。具体操作可以看书上的例子,比较通俗易懂。
预测分析法是递归下降分析方法 (recursive-descent parsing) 的一种,Rust 的编译器就是用的递归下降分析法,书上的例子也大致描述了它是如何运作的。
两年前注册了这个双拼域名,一开始是有买 VPS 打算弄个网站,而且也存在了一段时间。然而 VPS 贵,而且懒啊,所以后来就没续费了。那么,就用 Github Pages 随便搭个静态博客,一般情况下是什么都不会来写的,如果心情好可以随便发点文字。
嗯…就酱~
上次看书看到了第二章,书中实现了一个简单的中缀到后缀的翻译器。这个例程在书中图2-27,本身是 Java 代码,如果用 C 或 C++ 来实现,那是相当简单的,几乎就是抄过来,正好我也在学习 Rust 语言,于是想到了使用 Rust 来实现书中的例程。
这个翻译器只能处理一位数字间不带括号的加减法,其产生式为:
expr -> expr + term
expr -> expr - term
term -> [0-9] # 直接使用正则来节省篇幅,就不展开了
代码在 gist 上保存了,也搬运在这里:
use std::io::{Read, Error, ErrorKind};
use std::fmt;
enum ParseError {
SyntaxError,
Parse(Error)
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ParseError::SyntaxError => write!(f, "syntax error"),
ParseError::Parse(ref e) => e.fmt(f),
}
}
}
struct Parser {
lookahead: u8,
}
impl Parser {
pub fn new() -> Self {
let ch = read_char().expect("Cannot read charactor from input!");
Self {
lookahead: ch,
}
}
pub fn expr(&mut self) -> Result<(), ParseError> {
if let Err(e) = self.term() {
return Err(e);
}
loop {
if self.lookahead == b'+' {
if let Err(e) = self.pmatch(b'+') {
return Err(e);
}
if let Err(e) = self.term() {
return Err(e);
}
print!("+");
} else if self.lookahead == b'-' {
if let Err(e) = self.pmatch(b'-') {
return Err(e);
}
if let Err(e) = self.term() {
return Err(e);
}
print!("-");
} else {
return Ok(());
}
}
}
pub fn term(&mut self) -> Result<(), ParseError> {
if self.lookahead.is_ascii_digit() {
print!("{}", char::from(self.lookahead));
self.pmatch(self.lookahead)
} else {
Err(ParseError::SyntaxError)
}
}
pub fn pmatch(&mut self, t: u8) -> Result<(), ParseError> {
if self.lookahead == t {
let input = read_char();
match input {
Ok(ch) => {
self.lookahead = ch;
Ok(())
},
Err(e) => Err(ParseError::Parse(e)),
}
} else {
Err(ParseError::SyntaxError)
}
}
}
fn read_char() -> Result<u8, Error> {
std::io::stdin().bytes().next()
.unwrap_or(Err(Error::new(ErrorKind::Other, "Cannot read from stdin!")))
}
fn main() {
let mut parse = Parser::new();
match parse.expr() {
Ok(_) => println!(""),
Err(e) => println!("{}", e),
};
}
顺手也写了一个 Python 版本:
import readchar
class Parser:
def __init__(self):
self.lookahead = readchar.readchar()
def expr(self):
self.term()
while True:
if self.lookahead == b'+':
self.match(b'+')
self.term()
print('+', end='')
elif self.lookahead == b'-':
self.match(b'-')
self.term()
print('-', end='')
else:
return
def term(self):
if self.lookahead.isdigit():
print(self.lookahead.decode("utf-8"), end='')
self.match(self.lookahead)
else:
raise Exception("syntax error")
def match(self, t):
if self.lookahead == t:
self.lookahead = readchar.readchar()
else:
raise Exception("syntax error")
if __name__ == '__main__':
parse = Parser()
parse.expr()
print("")
记录一下是如何设置 Home Assistant 的吧。没有什么难点的部分就不写了,反正 Google 一下就找得到。
Google 之。
最简配置,添加到 configuration.yaml
里面。
homekit:
auto_start: False
自动启动关闭,否则 HomeKit 组件在设备还未接入完全的时候启动,在家庭 App 里就找不到设备。
相反,在 automations.yaml
里面加入以下代码,在 Home Assistant 启动五分钟后再启动 HomeKit 组件。
- id: '$IDOFAUTOMATION'
alias: Start HomeKit
trigger:
- event: start
platform: homeassistant
condition: []
action:
- delay: 00:05
- alias: ''
data: {}
service: homekit.start
Yeelight 吸顶灯可以在 App 里打开局域网控制,在启动 Home Assistant 的时候就会自动搜寻并添加,五分钟后启动 HomeKit 就能在 App 上出现了。
插件地址 caiyun.py。复制到 custom_components/sensor/
里面,然后在 configuration.yaml
配置
sensor:
- platform: caiyun
也可以顺便把默认的天气插件 yr
关掉。
由于前期调查没做好,买了个不支持局域网控制协议的版本,无法将其当作网关加入 Home Assistant,只能当作空调加入。
首先获取设备的 token,参考这篇教程。
然后使用这个插件添加,使用方法见其 README。
获取 token 的方法与空调伴侣相同,然后参照官方教程添加到 Home Assistant。值得注意的是,扫地机器人不适用于 HomeKit 组件,有待以后研究其他解决方案。
最后看看成果吧:
最近偶然得知了一个用 Free Pascal 写的编辑器 CudaText,便安装来玩玩。
轻量编辑器我一直都是使用 Sublime Text,而且重度依赖从终端命令启动编辑器打开文本,Sublime Text 就有一个官方的命令行可执行程序 subl.exe
。例如当前目录下有一个文本文档 file.txt
,我可以使用命令 subl file.txt
快捷打开,这个可执行程序会使用新的进程来运行 Sublime Text,因此当前的终端可以继续使用,不受影响。
但 CudaText 就不行了,虽然它的可执行程序 cudatext.exe
也支持参数,可以便捷打开文件或目录,但打开后会持续占用当前的终端,这一点令人很不爽。若是 *nix 系统,可在命令最后加一个 &
使其在后台运行,但 Windows PowerShell 似乎没有这样的功能。
Start-Process
创建新进程运行一个简单的方法是使用系统中已有的命令 Start-Process
来运行。还是刚才的例子:
Start-Process cudatext file.txt
# 或者使用别名
start cudatext file.txt
如果你尝试了上面的命令则会发现,对于 CudaText 这类占用终端的软件,即使是 Start-Process
也会产生一个 conhost 窗口。
所幸的是,Start-Process
支持 WindowStyle
参数,可以将其设置为 hidden
,就不会显示 conhost 窗口了。
当然,这样的命令不适合日常使用,但我们可以写一个脚本,这样就可以使用很短的命令来运行程序了。
# run.ps1
if ($args.Length -lt 1) {
exit 0
}
if ($args.Length -gt 1) {
$Arguments = $args[1..($args.Length - 1)] -join ' '
Start-Process -WindowStyle hidden $args[0] $Arguments
} else {
Start-Process -WindowStyle hidden $args[0]
}
把这个脚本另存为 run.ps1
,放到任意一个 $env:Path
目录中,就可以使用 run cudatext file.txt
快捷打开了。虽然这个脚本很短,但菜鸡如我,一开始写了一个很长的版本,边写边查给改成这样的。
对于文本编辑器这种常用程序,可以更进一步为其编写一个专用脚本
# ct.ps1
if ($args.Length -lt 1) {
Start-Process -WindowStyle hidden cudatext.exe
} else {
Start-Process -WindowStyle hidden cudatext.exe ($args -join ' ')
}
这样便可使用 ct file.txt
来打开了。
在写脚本的过程中,也想过写成一个函数放到 $PROFILE
里,但始终没搞定参数的问题,只好作罢,如果有夶䎜对 PowerShell 比较了解,请不吝赐教!
自从我配了台式机以后,始终缺一个可靠的音箱。买过 2.0, 也买过 2.1 声道的,最终结果就是其中某个坏掉,或者整个都不行了。其实我对音箱的要求真的不高,能正常出声音、稳定、方便就行了。我也用过小米的蓝牙音箱连接电脑,可靠是可靠,就是连接不怎么方便。
后来机缘巧合入手了一个 JBL go 2 音箱,虽然是个蓝牙音箱,但同时支持 AUX 连接电脑,这才部分解决了方便性的问题。为什么说部分呢?因为这音箱有个脑残的“功能”——如果一段时间内没有播放任何声音,就会自动关机。
这个功能对于作为便携蓝牙音箱的情形还算是有用,自动关机节省电量,有需求的时候再开机。但是我是拿它当台式机音箱,充电线是始终连着的,不缺这点电呀……
如何让这个音箱保持活跃呢?很简单,让它在关机之前播放声音就好了。通过多方搜索,我找到了 求问如何避免蓝牙音箱自动关机。? - 66666的回答 - 知乎 这个答案。
那么接下来就简单了,写了个 AHK 脚本,每分钟让系统发出一个人耳察觉不到的声音,顺利解决这个问题了!
Loop {
RunWait, "%USERPROFILE%\scoop\apps\nircmd\current\nircmd.exe" beep 30 100 ;
sleep, 60000 ;
}
编译成可执行文件放到启动里,电脑不关机,音箱就会一直待命了。
近日在处理文件差异的时候搜索到的一个方法,可以用 vim 把差异导出为 HTML (来源)
vimdiff -c TOhtml -c "w vimdiff_export.html | qa!" file1 file2
使用 vimdiff
对比两个文件,依次执行 TOhtml
、保存、全部退出。
2024-04-20 更新
Vim 保存的网页在表格中使用了左右两个 <div>
,可以自然地同步纵向的滚动,但横向的滚动并不同步。搜索一番后,我在 stackoverflow 上找到了这个回答。稍作修改,把如下代码放到 body
的末尾,就能同步两边的横向滚动了。
<script type="text/javascript">
var isSyncingLeftScroll = false;
var isSyncingRightScroll = false;
var leftDiv = document.getElementsByTagName('div')[0];
var rightDiv = document.getElementsByTagName('div')[1];
leftDiv.onscroll = function() {
if (!isSyncingLeftScroll) {
isSyncingRightScroll = true;
rightDiv.scrollLeft = this.scrollLeft;
}
isSyncingLeftScroll = false;
}
rightDiv.onscroll = function() {
if (!isSyncingRightScroll) {
isSyncingLeftScroll = true;
leftDiv.scrollLeft = this.scrollLeft;
}
isSyncingRightScroll = false;
}
</script>
虽然这段代码已经够短了,但我还是把它缩得更短了:
<script>m=!1,s=!1,a=document.getElementsByTagName("div"),l=a[0],r=a[1];l.onscroll=function(){m||(s=!0,r.scrollLeft=l.scrollLeft),m=!1},r.onscroll=function(){s||(m=!0,l.scrollLeft=r.scrollLeft),s=!1}</script>
这样,在我的 shell 脚本里,只需三行就可以满足需求:
SRC='<script>m=!1,s=!1,a=document.getElementsByTagName("div"),l=a[0],r=a[1];l.onscroll=function(){m||(s=!0,r.scrollLeft=l.scrollLeft),m=!1},r.onscroll=function(){s||(m=!0,l.scrollLeft=r.scrollLeft),s=!1}</script>'
vimdiff -c TOhtml -c "w vimdiff_export.html | qa!" file1 file2
sed -i '/<\/body>/i'"${SRC}" vimdiff_export.html
距离上次看书已经过了很久了。这次看了书的 2.6 节,也花了很长时间。其实看书没花太久,因为没怎么看懂,反而实现例程用了很长时间,一方面是我没看懂书,另一方面是因为我的 Rust 写得太少了,最后写出来的代码也比较丑陋……
整个 2.6 节讲的就是如何进行词法分析,也就是怎么实现词法分析器。包括了剔除空白和注释、预读、字面量(书里写的是“常量”,但我感觉用字面量更好)、关键字和标识符。
这个是比较抽象的概念,就目前接触到的部分,我感觉词法单元就是组成语句或表达式的基本元素,比如:数字、标识符、关键字等。词法单元再分解就是单个字符了,因此我们的程序需要有能力把词法单元给提取出来。
在实现的时候,一个词法单元至少要有两个部分,其一是标签 (Tag),用来表示这是个啥,另一个是就是这个词法单元的内容,可以是值(当其为数字时)、可以是词素 (lexem) (当其为标识符时)。我在改写例程的时候定义了一个名为 Token
的 trait,然后用 Box<dyn Token>
来表示一个词法单元。代码是写出来了,但我不确定是不是用枚举来表示更好。
决定使用 trait 对象的一个原因是,书中的例程把词法单元的标签和内容(值或词素)声明为 public final
,大概意思是可读,但初始化后不可写。而 Rust 没有 final
这个东西,我立马就想到了把 "getter" 公开,而不提供 "setter",只在关联函数 new
里初始化,而枚举好像不能这样。
4月5日更新
参考了一个 Rust 写的 c11 词法分析器 c_lexer,用枚举应该是更好的选择,于是我重新写了一遍这节的练习,也更改了其他一些地方,比如书中对于符号的处理,是直接把字符对应的 ASCII 码作为其标签,我参照 c_lexer 给这一节涉及到的符号添加了枚举变体。
对于空白来讲,其它的都好说,遇到就跳过,但换行需要特别注意。首先是换行需要把行号加一,其次是 Windows 的换行是 \r\n
而不是 \n
,需要特殊处理(书中没处理)。
剔除注释在书上没给例程,留作练习 2.6.1 了,在我的代码里也丑陋地实现了。
我们读取代码的方式是一个字符一个字符地读,所以在遇到 <=
和 <
这种符号的时候,必须预读下一个字符才能确定,这很好理解。而在实现的时候,我用了 Rust 里的 Peekable
来帮助我在不移动迭代器的情况下读取下一个字符。而书中也讲了,今后会用到一个缓冲技术来实现预读。
4月5日更新
c_lexer 使用了状态机来做预读,我看了看目录,应该会在第三章讲到,现在就还是用老方法来做。不过我稍微改了一下,增加使用回移“指针”来实现一个缓冲区。
这节接触到的字面量只有无符号整数,在练习 2.6.3 会要求扩展到浮点数,但我还没做。对于无符号整数,词法单元里没有词素,而是直接保存了它的值。在我的代码里是用了一个结构体,实现 Token
这个 trait 来表示数字。
书中用了一个散列表来储存所有关键字和用到的标识符。我遇到的问题主要是 HashMap
的键 (K
) 以及值 (V
) 中的词素 (lexem) 用什么类型。在 Java 里面太好办了,有垃圾回收,用字符串就好了;但 Rust 里如果用字符串切片的引用,就要考虑生命周期和所有权的问题,到底是键持有字符串,还是值里的词素持有字符串呢?我想了半天没想明白,也不知道怎么实现把字符串移动进 HashMap
的键/值的同时,拿到它的引用,并放到同一项的值/键里……最后我放弃使用字符串切片的引用,让所有地方都持有一个字符串了,等以后知识储备够了再来解决。
4月5日更新
在 c_lexer 里用了 internship 里的
IStr
储存标识符的字符串部分,这就相当于有一个全局的HashSet<Rc<str>>
,但只需当作String
用就行了。如此一来,就不需要用HashMap
来储存非关键字的标识符,因为其字符串部分已经用IStr
实现了全局(线程内)唯一。另一个改变是对于关键字,不用
HashMap
,而用了一个完美散列 phf 的Map
。好处是有一个宏phf_map
可以方便地添加所有关键字,而不需要在初始化时一次次调用reserve
来储存。
英文名也叫 lexer,就是这一节的内容合起来实现的东西。读书的时候我是没读懂,写代码的时候也似懂非懂,写笔记的时候对照代码再顺了几遍稍微有点理解了。
代码在此:lexer.rs。
目前代码只做到了例程(书中图 2-34 + 图 2-35)和练习 2.6.1 的内容。之后再继续把练习 2.6.2 和 2.6.3 也做了,就直接更新 gist 了。
4月5日更新
新的代码实现了所有练习,就不放在 gist 了,我搞了个仓库:dragon-book-lexer。跟之前的比起来,多了
lex
方法,可以一次次调用scan
,最终生成词法单元的向量。
如果你因为某种原因,不得不使用新版的vscode远程连接到无法更新glibc的系统,可以使用链接中的方法去掉顶部恼人的提醒
使用了 Gmeek 作为框架,以前的(没什么营养的)文章慢慢搬运吧。
社区版 eMule 可以在 https://github.com/irwir/eMule/releases 页面下载,虽然源代码里有中文翻译的资源,但发布的二进制文件并没有将其编译,因此需要自行编译来使用。
因为我一般只安装 Visual Studio 生成工具,所以记录一下我用命令行编译的方法。如果没有安装 MFC 相关模块,则需要把 zh_CN.rc
里引用 afxres.h
改为 引用 Windows.h
和 winres.h
1。然后,使用命令 msbuild zh_CN.vcxproj /p:configuration=Dynamic /p:platform=x64
来编译2。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.