<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>typeof.net</title>
	<atom:link href="http://typeof.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://typeof.net</link>
	<description>Language matters</description>
	<lastBuildDate>Tue, 17 Jan 2012 17:11:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Eisa 近况</title>
		<link>http://typeof.net/2011/12/eisa-recents-1/</link>
		<comments>http://typeof.net/2011/12/eisa-recents-1/#comments</comments>
		<pubDate>Thu, 15 Dec 2011 11:06:46 +0000</pubDate>
		<dc:creator>Belleve Invis</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[中文]]></category>

		<guid isPermaLink="false">http://typeof.net/?p=371</guid>
		<description><![CDATA[巨坑地址：http://github.com/infinte/eisa 把「两个」代码生成器合并了不少，主要是表达式的部分。原来 LFC 里，非阻塞的函数和连续体的代码生成器是分开的，然而很多段代码都大同小异（就是 transform 和 expPart 的区别，这两个函数都是进行递归变换的函数，不过行为不同），现在合并了。 现在的 LFC 里，全局变量必须手工指定，以后会简化设置它的方法，比如允许 JavaScript 全局变量直接绑定到 lofn 的顶层函数里 文档这个巨坑慢慢填罢 顺便贴个东西：用源码的面积计算 pi 复刻版： var c = 0; var l = 0 def x(n) = (c += (2 if (n == x), 1)) then (l = n if(l &#60; n), l) then (n + 1) def x.valueOf = {1} x [...]]]></description>
			<content:encoded><![CDATA[
<p>巨坑地址：http://github.com/infinte/eisa</p>
<ul>
	<li>把「两个」代码生成器合并了不少，主要是表达式的部分。原来 LFC 里，非阻塞的函数和连续体的代码生成器是分开的，然而很多段代码都大同小异（就是 transform 和 expPart 的区别，这两个函数都是进行递归变换的函数，不过行为不同），现在合并了。</li>
	<li>现在的 LFC 里，全局变量必须手工指定，以后会简化设置它的方法，比如允许 JavaScript 全局变量直接绑定到 lofn 的顶层函数里</li>
	<li>文档这个巨坑慢慢填罢</li>
</ul>
<p>顺便贴个东西：用源码的面积计算 pi 复刻版：</p>
<pre>var c = 0; var l = 0
def x(n) = (c += (2 if (n == x), 1)) then (l = n if(l &lt; n), l) then (n + 1)
def x.valueOf = {1}
              x x x x x
         x x x x x x x x x x
      x x x x x x x x x x x x x
   x x x x x x x x x x x x x x x x
  x x x x x x x x x x x x x x x x x
  x x x x x x x x x x x x x x x x x
 x x x x x x x x x x x x x x x x x x
 x x x x x x x x x x x x x x x x x x
 x x x x x x x x x x x x x x x x x x
 x x x x x x x x x x x x x x x x x x
  x x x x x x x x x x x x x x x x x
  x x x x x x x x x x x x x x x x x
   x x x x x x x x x x x x x x x x
      x x x x x x x x x x x x x
          x x x x x x x x x
              x x x x x
4 * c / l / l // result</pre>

<p>Edit: 果然我很习惯改语法…… <code>~~</code> 改成 <code>then</code> 了</p>]]></content:encoded>
			<wfw:commentRss>http://typeof.net/2011/12/eisa-recents-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>0x5f3759df 是怎么算出来的？</title>
		<link>http://typeof.net/2011/11/how-0x5f3759df-calculated/</link>
		<comments>http://typeof.net/2011/11/how-0x5f3759df-calculated/#comments</comments>
		<pubDate>Wed, 16 Nov 2011 13:59:16 +0000</pubDate>
		<dc:creator>Belleve Invis</dc:creator>
				<category><![CDATA[Depth]]></category>
		<category><![CDATA[Discussion]]></category>
		<category><![CDATA[中文]]></category>
		<category><![CDATA[algorithm]]></category>
		<category><![CDATA[calculation]]></category>
		<category><![CDATA[math]]></category>

		<guid isPermaLink="false">http://typeof.net/?p=363</guid>
		<description><![CDATA[（注：本文包含数学公式，使用 SVG 图形格式，请不要在 RSS 阅读器里阅读本文） 基本上每个程序员都会对 Quake 那个神一样的常量 0x5f3759df 感到惊奇。在 Quake 里，有一段代码是计算光照时候使用的函数，计算 ，那段代码写的真叫经典： float Q_rsqrt( float number ){ long i; float x2, y; const float threehalfs = 1.5F; x2 = number * 0.5F; y = number; i = * ( long * ) &#38;y; // evil floating point bit level hacking i = 0x5f3759df - [...]]]></description>
			<content:encoded><![CDATA[<svg style="display: none"><defs>
<path d="M4.008 -3.192C3.656 -3.104 3.64 -2.792 3.64 -2.76C3.64 -2.584 3.776 -2.464 3.952 -2.464S4.4 -2.6 4.4 -2.944C4.4 -3.4 3.896 -3.528 3.6 -3.528C3.224 -3.528 2.92 -3.264 2.736 -2.952C2.56 -3.376 2.144 -3.528 1.816 -3.528C0.944 -3.528 0.456 -2.528 0.456 -2.304C0.456 -2.232 0.512 -2.2 0.576 -2.2C0.672 -2.2 0.688 -2.24 0.712 -2.336C0.896 -2.92 1.376 -3.304 1.792 -3.304C2.104 -3.304 2.256 -3.08 2.256 -2.792C2.256 -2.632 2.16 -2.264 2.096 -2.008C2.04 -1.776 1.864 -1.064 1.824 -0.912C1.712 -0.48 1.424 -0.144 1.064 -0.144C1.032 -0.144 0.824 -0.144 0.656 -0.256C1.024 -0.344 1.024 -0.68 1.024 -0.688C1.024 -0.872 0.88 -0.984 0.704 -0.984C0.488 -0.984 0.256 -0.8 0.256 -0.496C0.256 -0.128 0.648 0.08 1.056 0.08C1.48 0.08 1.776 -0.24 1.92 -0.496C2.096 -0.104 2.464 0.08 2.848 0.08C3.72 0.08 4.2 -0.92 4.2 -1.144C4.2 -1.224 4.136 -1.248 4.08 -1.248C3.984 -1.248 3.968 -1.192 3.944 -1.112C3.784 -0.576 3.328 -0.144 2.864 -0.144C2.6 -0.144 2.408 -0.32 2.408 -0.656C2.408 -0.816 2.456 -1 2.568 -1.448C2.624 -1.688 2.8 -2.392 2.84 -2.544C2.952 -2.96 3.232 -3.304 3.592 -3.304C3.632 -3.304 3.84 -3.304 4.008 -3.192Z" id="gtexh5f3759df_1"/>
<path d="M2.472 -3.516C2.496 -3.588 2.796 -4.188 3.24 -4.572C3.552 -4.86 3.96 -5.052 4.428 -5.052C4.908 -5.052 5.076 -4.692 5.076 -4.212C5.076 -4.14 5.076 -3.9 4.932 -3.336L4.632 -2.1C4.536 -1.74 4.308 -0.852 4.284 -0.72C4.236 -0.54 4.164 -0.228 4.164 -0.18C4.164 -0.012 4.296 0.12 4.476 0.12C4.836 0.12 4.896 -0.156 5.004 -0.588L5.724 -3.456C5.748 -3.552 6.372 -5.052 7.692 -5.052C8.172 -5.052 8.34 -4.692 8.34 -4.212C8.34 -3.54 7.872 -2.232 7.608 -1.512C7.5 -1.224 7.44 -1.068 7.44 -0.852C7.44 -0.312 7.812 0.12 8.388 0.12C9.504 0.12 9.924 -1.644 9.924 -1.716C9.924 -1.776 9.876 -1.824 9.804 -1.824C9.696 -1.824 9.684 -1.788 9.624 -1.584C9.348 -0.624 8.904 -0.12 8.424 -0.12C8.304 -0.12 8.112 -0.132 8.112 -0.516C8.112 -0.828 8.256 -1.212 8.304 -1.344C8.52 -1.92 9.06 -3.336 9.06 -4.032C9.06 -4.752 8.64 -5.292 7.728 -5.292C6.924 -5.292 6.276 -4.836 5.796 -4.128C5.76 -4.776 5.364 -5.292 4.464 -5.292C3.396 -5.292 2.832 -4.536 2.616 -4.236C2.58 -4.92 2.088 -5.292 1.56 -5.292C1.212 -5.292 0.936 -5.124 0.708 -4.668C0.492 -4.236 0.324 -3.504 0.324 -3.456S0.372 -3.348 0.456 -3.348C0.552 -3.348 0.564 -3.36 0.636 -3.636C0.816 -4.344 1.044 -5.052 1.524 -5.052C1.8 -5.052 1.896 -4.86 1.896 -4.5C1.896 -4.236 1.776 -3.768 1.692 -3.396L1.356 -2.1C1.308 -1.872 1.176 -1.332 1.116 -1.116C1.032 -0.804 0.9 -0.24 0.9 -0.18C0.9 -0.012 1.032 0.12 1.212 0.12C1.356 0.12 1.524 0.048 1.62 -0.132C1.644 -0.192 1.752 -0.612 1.812 -0.852L2.076 -1.932L2.472 -3.516Z" id="gtexh5f3759df_2"/>
<path d="M3.296 6.76L1.784 3.656C1.744 3.568 1.712 3.528 1.648 3.528C1.616 3.528 1.6 3.536 1.52 3.592L0.704 4.16C0.592 4.232 0.592 4.272 0.592 4.296C0.592 4.344 0.632 4.408 0.704 4.408C0.736 4.408 0.752 4.408 0.848 4.328C0.952 4.264 1.112 4.144 1.248 4.048L2.928 7.496C3 7.64 3.032 7.64 3.112 7.64C3.248 7.64 3.272 7.6 3.336 7.472L7.2 -0.024C7.264 -0.136 7.264 -0.152 7.264 -0.184C7.264 -0.28 7.184 -0.368 7.08 -0.368S6.944 -0.304 6.888 -0.2L3.296 6.76Z" id="gtexh5f3759df_3"/>
<path d="M2.512 -5.096C2.512 -5.312 2.496 -5.32 2.28 -5.32C1.952 -5 1.528 -4.808 0.768 -4.808V-4.544C0.984 -4.544 1.416 -4.544 1.88 -4.76V-0.656C1.88 -0.36 1.856 -0.264 1.096 -0.264H0.816V0C1.144 -0.024 1.832 -0.024 2.192 -0.024S3.248 -0.024 3.576 0V-0.264H3.296C2.536 -0.264 2.512 -0.36 2.512 -0.656V-5.096Z" id="gtexh5f3759df_4"/>
<path d="M4.392 -7.38C4.5 -7.824 4.548 -7.848 5.016 -7.848H6.576C7.932 -7.848 7.932 -6.696 7.932 -6.588C7.932 -5.616 6.96 -4.38 5.376 -4.38H3.648L4.392 -7.38ZM6.42 -4.284C7.728 -4.524 8.916 -5.436 8.916 -6.54C8.916 -7.476 8.088 -8.196 6.732 -8.196H2.88C2.652 -8.196 2.544 -8.196 2.544 -7.968C2.544 -7.848 2.652 -7.848 2.832 -7.848C3.564 -7.848 3.564 -7.752 3.564 -7.62C3.564 -7.596 3.564 -7.524 3.516 -7.344L1.896 -0.888C1.788 -0.468 1.764 -0.348 0.924 -0.348C0.696 -0.348 0.576 -0.348 0.576 -0.132C0.576 0 0.648 0 0.888 0H5.004C6.84 0 8.256 -1.392 8.256 -2.604C8.256 -3.588 7.392 -4.188 6.42 -4.284ZM4.716 -0.348H3.096C2.928 -0.348 2.904 -0.348 2.832 -0.36C2.7 -0.372 2.688 -0.396 2.688 -0.492C2.688 -0.576 2.712 -0.648 2.736 -0.756L3.576 -4.14H5.832C7.248 -4.14 7.248 -2.82 7.248 -2.724C7.248 -1.572 6.204 -0.348 4.716 -0.348Z" id="gtexh5f3759df_5"/>
<path d="M3.456 -7.692C3.456 -7.968 3.456 -7.98 3.216 -7.98C2.928 -7.656 2.328 -7.212 1.092 -7.212V-6.864C1.368 -6.864 1.968 -6.864 2.628 -7.176V-0.924C2.628 -0.492 2.592 -0.348 1.536 -0.348H1.164V0C1.488 -0.024 2.652 -0.024 3.048 -0.024S4.596 -0.024 4.92 0V-0.348H4.548C3.492 -0.348 3.456 -0.492 3.456 -0.924V-7.692Z" id="gtexh5f3759df_6"/>
<path d="M5.28 -2.016H5.016C4.98 -1.812 4.884 -1.152 4.764 -0.96C4.68 -0.852 3.996 -0.852 3.636 -0.852H1.416C1.74 -1.128 2.472 -1.896 2.784 -2.184C4.608 -3.864 5.28 -4.488 5.28 -5.676C5.28 -7.056 4.188 -7.98 2.796 -7.98S0.588 -6.792 0.588 -5.76C0.588 -5.148 1.116 -5.148 1.152 -5.148C1.404 -5.148 1.716 -5.328 1.716 -5.712C1.716 -6.048 1.488 -6.276 1.152 -6.276C1.044 -6.276 1.02 -6.276 0.984 -6.264C1.212 -7.08 1.86 -7.632 2.64 -7.632C3.66 -7.632 4.284 -6.78 4.284 -5.676C4.284 -4.656 3.696 -3.768 3.012 -3L0.588 -0.288V0H4.968L5.28 -2.016Z" id="gtexh5f3759df_7"/>
<path d="M5.7 -7.452V-7.728H2.808C1.356 -7.728 1.332 -7.884 1.284 -8.112H1.02L0.648 -5.712H0.912C0.948 -5.928 1.056 -6.672 1.212 -6.804C1.308 -6.876 2.208 -6.876 2.376 -6.876H4.92L3.648 -5.052C3.324 -4.584 2.112 -2.616 2.112 -0.36C2.112 -0.228 2.112 0.252 2.604 0.252C3.108 0.252 3.108 -0.216 3.108 -0.372V-0.972C3.108 -2.76 3.396 -4.152 3.96 -4.956L5.7 -7.452Z" id="gtexh5f3759df_8"/>
<path d="M8.1 -3.888C8.268 -3.888 8.484 -3.888 8.484 -4.104C8.484 -4.332 8.28 -4.332 8.1 -4.332H1.032C0.864 -4.332 0.648 -4.332 0.648 -4.116C0.648 -3.888 0.852 -3.888 1.032 -3.888H8.1ZM8.1 -1.656C8.268 -1.656 8.484 -1.656 8.484 -1.872C8.484 -2.1 8.28 -2.1 8.1 -2.1H1.032C0.864 -2.1 0.648 -2.1 0.648 -1.884C0.648 -1.656 0.852 -1.656 1.032 -1.656H8.1Z" id="gtexh5f3759df_9"/>
<path d="M2.412 -4.824H3.516C3.744 -4.824 3.864 -4.824 3.864 -5.04C3.864 -5.172 3.792 -5.172 3.552 -5.172H2.496L2.94 -6.924C2.988 -7.092 2.988 -7.116 2.988 -7.2C2.988 -7.392 2.832 -7.5 2.676 -7.5C2.58 -7.5 2.304 -7.464 2.208 -7.08L1.74 -5.172H0.612C0.372 -5.172 0.264 -5.172 0.264 -4.944C0.264 -4.824 0.348 -4.824 0.576 -4.824H1.644L0.852 -1.656C0.756 -1.236 0.72 -1.116 0.72 -0.96C0.72 -0.396 1.116 0.12 1.788 0.12C3 0.12 3.648 -1.632 3.648 -1.716C3.648 -1.788 3.6 -1.824 3.528 -1.824C3.504 -1.824 3.456 -1.824 3.432 -1.776C3.42 -1.764 3.408 -1.752 3.324 -1.56C3.072 -0.96 2.52 -0.12 1.824 -0.12C1.464 -0.12 1.44 -0.42 1.44 -0.684C1.44 -0.696 1.44 -0.924 1.476 -1.068L2.412 -4.824Z" id="gtexh5f3759df_10"/>
<path d="M2.256 -1.632C2.384 -1.752 2.72 -2.016 2.848 -2.128C3.344 -2.584 3.816 -3.024 3.816 -3.752C3.816 -4.704 3.016 -5.32 2.016 -5.32C1.056 -5.32 0.424 -4.592 0.424 -3.88C0.424 -3.488 0.736 -3.432 0.848 -3.432C1.016 -3.432 1.264 -3.552 1.264 -3.856C1.264 -4.272 0.864 -4.272 0.768 -4.272C1 -4.856 1.536 -5.056 1.928 -5.056C2.672 -5.056 3.056 -4.424 3.056 -3.752C3.056 -2.92 2.472 -2.312 1.528 -1.344L0.52 -0.304C0.424 -0.216 0.424 -0.2 0.424 0H3.584L3.816 -1.432H3.568C3.544 -1.272 3.48 -0.872 3.384 -0.72C3.336 -0.656 2.728 -0.656 2.6 -0.656H1.176L2.256 -1.632Z" id="gtexh5f3759df_11"/>
<path d="M3.9 2.916C3.9 2.88 3.9 2.856 3.696 2.652C2.496 1.44 1.824 -0.54 1.824 -2.988C1.824 -5.316 2.388 -7.32 3.78 -8.736C3.9 -8.844 3.9 -8.868 3.9 -8.904C3.9 -8.976 3.84 -9 3.792 -9C3.636 -9 2.652 -8.136 2.064 -6.96C1.452 -5.748 1.176 -4.464 1.176 -2.988C1.176 -1.92 1.344 -0.492 1.968 0.792C2.676 2.232 3.66 3.012 3.792 3.012C3.84 3.012 3.9 2.988 3.9 2.916Z" id="gtexh5f3759df_12"/>
<path d="M3.384 -2.988C3.384 -3.9 3.264 -5.388 2.592 -6.78C1.884 -8.22 0.9 -9 0.768 -9C0.72 -9 0.66 -8.976 0.66 -8.904C0.66 -8.868 0.66 -8.844 0.864 -8.64C2.064 -7.428 2.736 -5.448 2.736 -3C2.736 -0.672 2.172 1.332 0.78 2.748C0.66 2.856 0.66 2.88 0.66 2.916C0.66 2.988 0.72 3.012 0.768 3.012C0.924 3.012 1.908 2.148 2.496 0.972C3.108 -0.252 3.384 -1.548 3.384 -2.988Z" id="gtexh5f3759df_13"/>
<path d="M4.788 -2.772H8.1C8.268 -2.772 8.484 -2.772 8.484 -2.988C8.484 -3.216 8.28 -3.216 8.1 -3.216H4.788V-6.528C4.788 -6.696 4.788 -6.912 4.572 -6.912C4.344 -6.912 4.344 -6.708 4.344 -6.528V-3.216H1.032C0.864 -3.216 0.648 -3.216 0.648 -3C0.648 -2.772 0.852 -2.772 1.032 -2.772H4.344V0.54C4.344 0.708 4.344 0.924 4.56 0.924C4.788 0.924 4.788 0.72 4.788 0.54V-2.772Z" id="gtexh5f3759df_14"/>
<path d="M1.428 -2.172C1.992 -1.8 2.472 -1.8 2.604 -1.8C3.684 -1.8 4.488 -2.616 4.488 -3.54C4.488 -3.864 4.392 -4.32 4.008 -4.704C4.476 -5.184 5.04 -5.184 5.1 -5.184C5.148 -5.184 5.208 -5.184 5.256 -5.16C5.136 -5.112 5.076 -4.992 5.076 -4.86C5.076 -4.692 5.196 -4.548 5.388 -4.548C5.484 -4.548 5.7 -4.608 5.7 -4.872C5.7 -5.088 5.532 -5.424 5.112 -5.424C4.488 -5.424 4.02 -5.04 3.852 -4.86C3.492 -5.136 3.072 -5.292 2.616 -5.292C1.536 -5.292 0.732 -4.476 0.732 -3.552C0.732 -2.868 1.152 -2.424 1.272 -2.316C1.128 -2.136 0.912 -1.788 0.912 -1.32C0.912 -0.624 1.332 -0.324 1.428 -0.264C0.876 -0.108 0.324 0.324 0.324 0.948C0.324 1.776 1.452 2.46 2.928 2.46C4.356 2.46 5.544 1.824 5.544 0.924C5.544 0.624 5.46 -0.084 4.74 -0.456C4.128 -0.768 3.528 -0.768 2.496 -0.768C1.764 -0.768 1.68 -0.768 1.464 -0.996C1.344 -1.116 1.236 -1.344 1.236 -1.596C1.236 -1.8 1.308 -2.004 1.428 -2.172ZM2.616 -2.052C1.56 -2.052 1.56 -3.264 1.56 -3.54C1.56 -3.756 1.56 -4.248 1.764 -4.572C1.992 -4.92 2.352 -5.04 2.604 -5.04C3.66 -5.04 3.66 -3.828 3.66 -3.552C3.66 -3.336 3.66 -2.844 3.456 -2.52C3.228 -2.172 2.868 -2.052 2.616 -2.052ZM2.94 2.208C1.788 2.208 0.912 1.62 0.912 0.936C0.912 0.84 0.936 0.372 1.392 0.06C1.656 -0.108 1.764 -0.108 2.604 -0.108C3.6 -0.108 4.956 -0.108 4.956 0.936C4.956 1.644 4.044 2.208 2.94 2.208Z" id="gtexh5f3759df_15"/>
<path d="M2.064 -8.328L0.396 -8.196V-7.848C1.212 -7.848 1.308 -7.764 1.308 -7.176V-0.888C1.308 -0.348 1.176 -0.348 0.396 -0.348V0C0.732 -0.024 1.32 -0.024 1.68 -0.024S2.64 -0.024 2.976 0V-0.348C2.208 -0.348 2.064 -0.348 2.064 -0.888V-8.328Z" id="gtexh5f3759df_16"/>
<path d="M5.508 -2.568C5.508 -4.116 4.332 -5.352 2.94 -5.352C1.5 -5.352 0.36 -4.08 0.36 -2.568C0.36 -1.032 1.56 0.12 2.928 0.12C4.344 0.12 5.508 -1.056 5.508 -2.568ZM2.94 -0.144C2.496 -0.144 1.956 -0.336 1.608 -0.924C1.284 -1.464 1.272 -2.172 1.272 -2.676C1.272 -3.132 1.272 -3.864 1.644 -4.404C1.98 -4.92 2.508 -5.112 2.928 -5.112C3.396 -5.112 3.9 -4.896 4.224 -4.428C4.596 -3.876 4.596 -3.12 4.596 -2.676C4.596 -2.256 4.596 -1.512 4.284 -0.948C3.948 -0.372 3.396 -0.144 2.94 -0.144Z" id="gtexh5f3759df_17"/>
<path d="M5.592 -1.816C5.72 -1.816 5.896 -1.816 5.896 -2S5.72 -2.184 5.592 -2.184H1.008C0.88 -2.184 0.704 -2.184 0.704 -2S0.88 -1.816 1.008 -1.816H5.592Z" id="gtexh5f3759df_18"/>
<path d="M3.378 -2.358C3.168 -2.298 3.12 -2.124 3.12 -2.034C3.12 -1.842 3.276 -1.8 3.36 -1.8C3.534 -1.8 3.708 -1.944 3.708 -2.178C3.708 -2.502 3.354 -2.646 3.048 -2.646C2.652 -2.646 2.412 -2.34 2.346 -2.226C2.268 -2.376 2.04 -2.646 1.59 -2.646C0.9 -2.646 0.492 -1.932 0.492 -1.722C0.492 -1.692 0.516 -1.638 0.6 -1.638S0.702 -1.674 0.72 -1.728C0.87 -2.214 1.278 -2.448 1.572 -2.448S1.956 -2.256 1.956 -2.058C1.956 -1.986 1.956 -1.932 1.908 -1.746C1.77 -1.188 1.638 -0.642 1.608 -0.57C1.518 -0.342 1.302 -0.138 1.05 -0.138C1.014 -0.138 0.846 -0.138 0.708 -0.228C0.942 -0.306 0.966 -0.504 0.966 -0.552C0.966 -0.708 0.846 -0.786 0.726 -0.786C0.558 -0.786 0.378 -0.654 0.378 -0.408C0.378 -0.066 0.756 0.06 1.038 0.06C1.38 0.06 1.626 -0.174 1.74 -0.36C1.86 -0.108 2.148 0.06 2.49 0.06C3.198 0.06 3.594 -0.666 3.594 -0.864C3.594 -0.876 3.588 -0.948 3.48 -0.948C3.396 -0.948 3.384 -0.906 3.366 -0.852C3.192 -0.33 2.766 -0.138 2.514 -0.138C2.286 -0.138 2.124 -0.27 2.124 -0.522C2.124 -0.636 2.154 -0.768 2.208 -0.978L2.4 -1.758C2.46 -1.992 2.49 -2.1 2.616 -2.244C2.7 -2.334 2.844 -2.448 3.036 -2.448C3.066 -2.448 3.246 -2.448 3.378 -2.358Z" id="gtexh5f3759df_19"/>
<path d="M1.352 -0.632C1.28 -0.328 1.264 -0.264 0.672 -0.264C0.52 -0.264 0.424 -0.264 0.424 -0.112C0.424 0 0.528 0 0.664 0H3.632C4.952 0 5.936 -0.936 5.936 -1.712C5.936 -2.296 5.432 -2.768 4.632 -2.856C5.56 -3.032 6.36 -3.64 6.36 -4.344C6.36 -4.944 5.776 -5.464 4.768 -5.464H1.976C1.832 -5.464 1.728 -5.464 1.728 -5.312C1.728 -5.2 1.824 -5.2 1.96 -5.2C2.224 -5.2 2.456 -5.2 2.456 -5.072C2.456 -5.04 2.448 -5.032 2.424 -4.928L1.352 -0.632ZM2.6 -2.952L3.088 -4.904C3.16 -5.176 3.168 -5.2 3.496 -5.2H4.648C5.432 -5.2 5.608 -4.688 5.608 -4.36C5.608 -3.68 4.88 -2.952 3.856 -2.952H2.6ZM2.048 -0.264C1.976 -0.28 1.952 -0.28 1.952 -0.336C1.952 -0.4 1.968 -0.464 1.984 -0.512L2.544 -2.728H4.168C4.912 -2.728 5.16 -2.224 5.16 -1.776C5.16 -0.992 4.392 -0.264 3.432 -0.264H2.048Z" id="gtexh5f3759df_20"/>
<path d="M1.608 -1.824C1.784 -1.824 2.384 -1.832 2.808 -1.984C3.504 -2.224 3.528 -2.704 3.528 -2.824C3.528 -3.272 3.104 -3.528 2.584 -3.528C1.68 -3.528 0.392 -2.816 0.392 -1.4C0.392 -0.584 0.888 0.08 1.768 0.08C3.016 0.08 3.688 -0.72 3.688 -0.832C3.688 -0.904 3.608 -0.96 3.56 -0.96S3.488 -0.936 3.448 -0.888C2.816 -0.144 1.92 -0.144 1.784 -0.144C1.2 -0.144 1.024 -0.64 1.024 -1.088C1.024 -1.328 1.096 -1.688 1.128 -1.824H1.608ZM1.192 -2.048C1.448 -3.024 2.176 -3.304 2.584 -3.304C2.904 -3.304 3.208 -3.144 3.208 -2.824C3.208 -2.048 1.896 -2.048 1.56 -2.048H1.192Z" id="gtexh5f3759df_21"/>
<path d="M5.688 -4.896C5.304 -4.824 5.16 -4.536 5.16 -4.308C5.16 -4.02 5.388 -3.924 5.556 -3.924C5.916 -3.924 6.168 -4.236 6.168 -4.56C6.168 -5.064 5.592 -5.292 5.088 -5.292C4.356 -5.292 3.948 -4.572 3.84 -4.344C3.564 -5.244 2.82 -5.292 2.604 -5.292C1.38 -5.292 0.732 -3.72 0.732 -3.456C0.732 -3.408 0.78 -3.348 0.864 -3.348C0.96 -3.348 0.984 -3.42 1.008 -3.468C1.416 -4.8 2.22 -5.052 2.568 -5.052C3.108 -5.052 3.216 -4.548 3.216 -4.26C3.216 -3.996 3.144 -3.72 3 -3.144L2.592 -1.5C2.412 -0.78 2.064 -0.12 1.428 -0.12C1.368 -0.12 1.068 -0.12 0.816 -0.276C1.248 -0.36 1.344 -0.72 1.344 -0.864C1.344 -1.104 1.164 -1.248 0.936 -1.248C0.648 -1.248 0.336 -0.996 0.336 -0.612C0.336 -0.108 0.9 0.12 1.416 0.12C1.992 0.12 2.4 -0.336 2.652 -0.828C2.844 -0.12 3.444 0.12 3.888 0.12C5.112 0.12 5.76 -1.452 5.76 -1.716C5.76 -1.776 5.712 -1.824 5.64 -1.824C5.532 -1.824 5.52 -1.764 5.484 -1.668C5.16 -0.612 4.464 -0.12 3.924 -0.12C3.504 -0.12 3.276 -0.432 3.276 -0.924C3.276 -1.188 3.324 -1.38 3.516 -2.172L3.936 -3.804C4.116 -4.524 4.524 -5.052 5.076 -5.052C5.1 -5.052 5.436 -5.052 5.688 -4.896Z" id="gtexh5f3759df_22"/>
<path d="M8.4 28.188C8.4 28.14 8.376 28.116 8.352 28.08C7.908 27.636 7.104 26.832 6.3 25.536C4.368 22.44 3.492 18.54 3.492 13.92C3.492 10.692 3.924 6.528 5.904 2.952C6.852 1.248 7.836 0.264 8.364 -0.264C8.4 -0.3 8.4 -0.324 8.4 -0.36C8.4 -0.48 8.316 -0.48 8.148 -0.48S7.956 -0.48 7.776 -0.3C3.756 3.36 2.496 8.856 2.496 13.908C2.496 18.624 3.576 23.376 6.624 26.964C6.864 27.24 7.32 27.732 7.812 28.164C7.956 28.308 7.98 28.308 8.148 28.308S8.4 28.308 8.4 28.188Z" id="gtexh5f3759df_23"/>
<path d="M6.324 13.92C6.324 9.204 5.244 4.452 2.196 0.864C1.956 0.588 1.5 0.096 1.008 -0.336C0.864 -0.48 0.84 -0.48 0.672 -0.48C0.528 -0.48 0.42 -0.48 0.42 -0.36C0.42 -0.312 0.468 -0.264 0.492 -0.24C0.912 0.192 1.716 0.996 2.52 2.292C4.452 5.388 5.328 9.288 5.328 13.908C5.328 17.136 4.896 21.3 2.916 24.876C1.968 26.58 0.972 27.576 0.468 28.08C0.444 28.116 0.42 28.152 0.42 28.188C0.42 28.308 0.528 28.308 0.672 28.308C0.84 28.308 0.864 28.308 1.044 28.128C5.064 24.468 6.324 18.972 6.324 13.92Z" id="gtexh5f3759df_24"/>
<path d="M1.248 26.124C1.632 26.1 1.836 25.836 1.836 25.536C1.836 25.14 1.536 24.948 1.26 24.948C0.972 24.948 0.672 25.128 0.672 25.548C0.672 26.16 1.272 26.664 2.004 26.664C3.828 26.664 4.512 23.856 5.364 20.376C6.288 16.584 7.068 12.756 7.716 8.904C8.16 6.348 8.604 3.948 9.012 2.4C9.156 1.812 9.564 0.264 10.032 0.264C10.404 0.264 10.704 0.492 10.752 0.54C10.356 0.564 10.152 0.828 10.152 1.128C10.152 1.524 10.452 1.716 10.728 1.716C11.016 1.716 11.316 1.536 11.316 1.116C11.316 0.468 10.668 0 10.008 0C9.096 0 8.424 1.308 7.764 3.756C7.728 3.888 6.096 9.912 4.776 17.76C4.464 19.596 4.116 21.6 3.72 23.268C3.504 24.144 2.952 26.4 1.98 26.4C1.548 26.4 1.26 26.124 1.248 26.124Z" id="gtexh5f3759df_25"/>
<path d="M7.908 -2.76C8.112 -2.76 8.328 -2.76 8.328 -3S8.112 -3.24 7.908 -3.24H1.416C1.212 -3.24 0.996 -3.24 0.996 -3S1.212 -2.76 1.416 -2.76H7.908Z" id="gtexh5f3759df_26"/>
<path d="M6.096 -4.524C6.252 -4.524 6.648 -4.524 6.648 -4.908C6.648 -5.172 6.42 -5.172 6.204 -5.172H3.552C1.752 -5.172 0.456 -3.168 0.456 -1.752C0.456 -0.732 1.116 0.12 2.196 0.12C3.612 0.12 5.16 -1.404 5.16 -3.204C5.16 -3.672 5.052 -4.128 4.764 -4.524H6.096ZM2.208 -0.12C1.596 -0.12 1.152 -0.588 1.152 -1.416C1.152 -2.136 1.584 -4.524 3.348 -4.524C3.864 -4.524 4.44 -4.272 4.44 -3.348C4.44 -2.928 4.248 -1.92 3.828 -1.224C3.396 -0.516 2.748 -0.12 2.208 -0.12Z" id="gtexh5f3759df_27"/>
<path d="M3.912 -2.552C3.912 -3.408 3.824 -3.928 3.56 -4.44C3.208 -5.144 2.56 -5.32 2.12 -5.32C1.112 -5.32 0.744 -4.568 0.632 -4.344C0.344 -3.76 0.328 -2.968 0.328 -2.552C0.328 -2.024 0.352 -1.216 0.736 -0.576C1.104 0.016 1.696 0.168 2.12 0.168C2.504 0.168 3.192 0.048 3.592 -0.744C3.888 -1.32 3.912 -2.032 3.912 -2.552ZM2.12 -0.056C1.848 -0.056 1.296 -0.184 1.128 -1.024C1.04 -1.48 1.04 -2.232 1.04 -2.648C1.04 -3.2 1.04 -3.76 1.128 -4.2C1.296 -5.016 1.92 -5.096 2.12 -5.096C2.392 -5.096 2.944 -4.96 3.104 -4.232C3.2 -3.792 3.2 -3.192 3.2 -2.648C3.2 -2.176 3.2 -1.456 3.104 -1.008C2.936 -0.168 2.384 -0.056 2.12 -0.056Z" id="gtexh5f3759df_28"/>
<path d="M2.208 -4.308C2.004 -4.296 1.956 -4.284 1.956 -4.176C1.956 -4.056 2.016 -4.056 2.232 -4.056H2.784C3.804 -4.056 4.26 -3.216 4.26 -2.064C4.26 -0.492 3.444 -0.072 2.856 -0.072C2.28 -0.072 1.296 -0.348 0.948 -1.14C1.332 -1.08 1.68 -1.296 1.68 -1.728C1.68 -2.076 1.428 -2.316 1.092 -2.316C0.804 -2.316 0.492 -2.148 0.492 -1.692C0.492 -0.624 1.56 0.252 2.892 0.252C4.32 0.252 5.376 -0.84 5.376 -2.052C5.376 -3.156 4.488 -4.02 3.336 -4.224C4.38 -4.524 5.052 -5.4 5.052 -6.336C5.052 -7.284 4.068 -7.98 2.904 -7.98C1.704 -7.98 0.816 -7.248 0.816 -6.372C0.816 -5.892 1.188 -5.796 1.368 -5.796C1.62 -5.796 1.908 -5.976 1.908 -6.336C1.908 -6.72 1.62 -6.888 1.356 -6.888C1.284 -6.888 1.26 -6.888 1.224 -6.876C1.68 -7.692 2.808 -7.692 2.868 -7.692C3.264 -7.692 4.044 -7.512 4.044 -6.336C4.044 -6.108 4.008 -5.436 3.66 -4.92C3.3 -4.392 2.892 -4.356 2.568 -4.344L2.208 -4.308Z" id="gtexh5f3759df_29"/>
<path d="M4.332 -7.812C4.332 -8.04 4.332 -8.1 4.164 -8.1C4.068 -8.1 4.032 -8.1 3.936 -7.956L0.324 -2.352V-2.004H3.48V-0.912C3.48 -0.468 3.456 -0.348 2.58 -0.348H2.34V0C2.616 -0.024 3.564 -0.024 3.9 -0.024S5.196 -0.024 5.472 0V-0.348H5.232C4.368 -0.348 4.332 -0.468 4.332 -0.912V-2.004H5.544V-2.352H4.332V-7.812ZM3.54 -6.876V-2.352H0.624L3.54 -6.876Z" id="gtexh5f3759df_30"/>
<path d="M4.392 -3.492C4.392 -0.66 3.132 -0.072 2.412 -0.072C2.124 -0.072 1.488 -0.108 1.188 -0.528H1.26C1.344 -0.504 1.776 -0.576 1.776 -1.02C1.776 -1.284 1.596 -1.512 1.284 -1.512S0.78 -1.308 0.78 -0.996C0.78 -0.252 1.38 0.252 2.424 0.252C3.924 0.252 5.376 -1.344 5.376 -3.948C5.376 -7.176 4.032 -7.98 2.976 -7.98C1.656 -7.98 0.492 -6.876 0.492 -5.292S1.608 -2.628 2.808 -2.628C3.696 -2.628 4.152 -3.276 4.392 -3.888V-3.492ZM2.856 -2.868C2.1 -2.868 1.776 -3.48 1.668 -3.708C1.476 -4.164 1.476 -4.74 1.476 -5.28C1.476 -5.952 1.476 -6.528 1.788 -7.02C2.004 -7.344 2.328 -7.692 2.976 -7.692C3.66 -7.692 4.008 -7.092 4.128 -6.816C4.368 -6.228 4.368 -5.208 4.368 -5.028C4.368 -4.02 3.912 -2.868 2.856 -2.868Z" id="gtexh5f3759df_31"/>
<path d="M3 3V2.556H1.836V-8.556H3V-9H1.392V3H3Z" id="gtexh5f3759df_32"/>
<path d="M1.86 -9H0.252V-8.556H1.416V2.556H0.252V3H1.86V-9Z" id="gtexh5f3759df_33"/>
<path d="M3.6 -8.196V-7.848C4.416 -7.848 4.512 -7.764 4.512 -7.176V-4.524C4.26 -4.872 3.744 -5.292 3.012 -5.292C1.62 -5.292 0.42 -4.116 0.42 -2.58C0.42 -1.056 1.56 0.12 2.88 0.12C3.792 0.12 4.32 -0.48 4.488 -0.708V0.12L6.18 0V-0.348C5.364 -0.348 5.268 -0.432 5.268 -1.02V-8.328L3.6 -8.196ZM4.488 -1.404C4.488 -1.188 4.488 -1.152 4.32 -0.888C4.032 -0.468 3.54 -0.12 2.94 -0.12C2.628 -0.12 1.332 -0.24 1.332 -2.568C1.332 -3.432 1.476 -3.912 1.74 -4.308C1.98 -4.68 2.46 -5.052 3.06 -5.052C3.804 -5.052 4.224 -4.512 4.344 -4.32C4.488 -4.116 4.488 -4.092 4.488 -3.876V-1.404Z" id="gtexh5f3759df_34"/>
<path d="M2.148 -2.784C2.472 -2.784 3.288 -2.808 3.864 -3.024C4.776 -3.372 4.86 -4.068 4.86 -4.284C4.86 -4.812 4.404 -5.292 3.612 -5.292C2.352 -5.292 0.54 -4.152 0.54 -2.016C0.54 -0.756 1.26 0.12 2.352 0.12C3.984 0.12 5.016 -1.152 5.016 -1.308C5.016 -1.38 4.944 -1.44 4.896 -1.44C4.86 -1.44 4.848 -1.428 4.74 -1.32C3.972 -0.3 2.832 -0.12 2.376 -0.12C1.692 -0.12 1.332 -0.66 1.332 -1.548C1.332 -1.716 1.332 -2.016 1.512 -2.784H2.148ZM1.572 -3.024C2.088 -4.872 3.228 -5.052 3.612 -5.052C4.14 -5.052 4.5 -4.74 4.5 -4.284C4.5 -3.024 2.58 -3.024 2.076 -3.024H1.572Z" id="gtexh5f3759df_35"/>
<path d="M8.34 -2.784C8.352 -2.82 8.388 -2.904 8.388 -2.952C8.388 -3.012 8.34 -3.072 8.268 -3.072C8.22 -3.072 8.196 -3.06 8.16 -3.024C8.136 -3.012 8.136 -2.988 8.028 -2.748C7.32 -1.068 6.804 -0.348 4.884 -0.348H3.132C2.964 -0.348 2.94 -0.348 2.868 -0.36C2.736 -0.372 2.724 -0.396 2.724 -0.492C2.724 -0.576 2.748 -0.648 2.772 -0.756L3.6 -4.068H4.788C5.724 -4.068 5.796 -3.864 5.796 -3.504C5.796 -3.384 5.796 -3.276 5.712 -2.916C5.688 -2.868 5.676 -2.82 5.676 -2.784C5.676 -2.7 5.736 -2.664 5.808 -2.664C5.916 -2.664 5.928 -2.748 5.976 -2.916L6.66 -5.7C6.66 -5.76 6.612 -5.82 6.54 -5.82C6.432 -5.82 6.42 -5.772 6.372 -5.604C6.132 -4.68 5.892 -4.416 4.824 -4.416H3.684L4.428 -7.368C4.536 -7.788 4.56 -7.824 5.052 -7.824H6.768C8.244 -7.824 8.544 -7.428 8.544 -6.516C8.544 -6.504 8.544 -6.168 8.496 -5.772C8.484 -5.724 8.472 -5.652 8.472 -5.628C8.472 -5.532 8.532 -5.496 8.604 -5.496C8.688 -5.496 8.736 -5.544 8.76 -5.76L9.012 -7.86C9.012 -7.896 9.036 -8.016 9.036 -8.04C9.036 -8.172 8.928 -8.172 8.712 -8.172H2.856C2.628 -8.172 2.508 -8.172 2.508 -7.956C2.508 -7.824 2.592 -7.824 2.796 -7.824C3.54 -7.824 3.54 -7.74 3.54 -7.608C3.54 -7.548 3.528 -7.5 3.492 -7.368L1.872 -0.888C1.764 -0.468 1.74 -0.348 0.9 -0.348C0.672 -0.348 0.552 -0.348 0.552 -0.132C0.552 0 0.624 0 0.864 0H6.888C7.152 0 7.164 -0.012 7.248 -0.204L8.34 -2.784Z" id="gtexh5f3759df_36"/>
<path d="M4.416 -7.308C4.524 -7.728 4.548 -7.848 5.424 -7.848C5.688 -7.848 5.784 -7.848 5.784 -8.076C5.784 -8.196 5.652 -8.196 5.616 -8.196C5.4 -8.196 5.136 -8.172 4.92 -8.172H3.444C3.204 -8.172 2.928 -8.196 2.688 -8.196C2.592 -8.196 2.46 -8.196 2.46 -7.968C2.46 -7.848 2.556 -7.848 2.796 -7.848C3.54 -7.848 3.54 -7.752 3.54 -7.62C3.54 -7.536 3.516 -7.464 3.492 -7.356L1.872 -0.888C1.764 -0.468 1.74 -0.348 0.864 -0.348C0.6 -0.348 0.492 -0.348 0.492 -0.12C0.492 0 0.612 0 0.672 0C0.888 0 1.152 -0.024 1.368 -0.024H2.844C3.084 -0.024 3.348 0 3.588 0C3.684 0 3.828 0 3.828 -0.216C3.828 -0.348 3.756 -0.348 3.492 -0.348C2.748 -0.348 2.748 -0.444 2.748 -0.588C2.748 -0.612 2.748 -0.672 2.796 -0.864L4.416 -7.308Z" id="gtexh5f3759df_37"/>
<path d="M4.404 -7.272C4.512 -7.728 4.548 -7.848 5.604 -7.848C5.928 -7.848 6.012 -7.848 6.012 -8.076C6.012 -8.196 5.88 -8.196 5.832 -8.196C5.592 -8.196 5.316 -8.172 5.076 -8.172H3.468C3.24 -8.172 2.976 -8.196 2.748 -8.196C2.652 -8.196 2.52 -8.196 2.52 -7.968C2.52 -7.848 2.628 -7.848 2.808 -7.848C3.54 -7.848 3.54 -7.752 3.54 -7.62C3.54 -7.596 3.54 -7.524 3.492 -7.344L1.872 -0.888C1.764 -0.468 1.74 -0.348 0.9 -0.348C0.672 -0.348 0.552 -0.348 0.552 -0.132C0.552 0 0.624 0 0.864 0H6.24C6.504 0 6.516 -0.012 6.6 -0.228L7.524 -2.784C7.548 -2.844 7.572 -2.916 7.572 -2.952C7.572 -3.024 7.512 -3.072 7.452 -3.072C7.44 -3.072 7.38 -3.072 7.356 -3.024C7.332 -3.012 7.332 -2.988 7.236 -2.76C6.852 -1.704 6.312 -0.348 4.284 -0.348H3.132C2.964 -0.348 2.94 -0.348 2.868 -0.36C2.736 -0.372 2.724 -0.396 2.724 -0.492C2.724 -0.576 2.748 -0.648 2.772 -0.756L4.404 -7.272Z" id="gtexh5f3759df_38"/>
<path d="M10.896 -7.32C11.004 -7.728 11.028 -7.848 11.88 -7.848C12.108 -7.848 12.216 -7.848 12.216 -8.076C12.216 -8.196 12.132 -8.196 11.904 -8.196H10.464C10.164 -8.196 10.152 -8.184 10.02 -7.992L5.64 -1.068L4.74 -7.932C4.704 -8.196 4.692 -8.196 4.38 -8.196H2.892C2.664 -8.196 2.556 -8.196 2.556 -7.968C2.556 -7.848 2.664 -7.848 2.844 -7.848C3.576 -7.848 3.576 -7.752 3.576 -7.62C3.576 -7.596 3.576 -7.524 3.528 -7.344L1.992 -1.224C1.848 -0.648 1.572 -0.384 0.768 -0.348C0.732 -0.348 0.588 -0.336 0.588 -0.132C0.588 0 0.696 0 0.744 0C0.984 0 1.596 -0.024 1.836 -0.024H2.412C2.58 -0.024 2.784 0 2.952 0C3.036 0 3.168 0 3.168 -0.228C3.168 -0.336 3.048 -0.348 3 -0.348C2.604 -0.36 2.22 -0.432 2.22 -0.864C2.22 -0.984 2.22 -0.996 2.268 -1.164L3.924 -7.776H3.936L4.932 -0.324C4.968 -0.036 4.98 0 5.088 0C5.22 0 5.28 -0.096 5.34 -0.204L10.164 -7.836H10.176L8.436 -0.888C8.328 -0.468 8.304 -0.348 7.464 -0.348C7.236 -0.348 7.116 -0.348 7.116 -0.132C7.116 0 7.224 0 7.296 0C7.5 0 7.74 -0.024 7.944 -0.024H9.36C9.564 -0.024 9.816 0 10.02 0C10.116 0 10.248 0 10.248 -0.228C10.248 -0.348 10.14 -0.348 9.96 -0.348C9.228 -0.348 9.228 -0.444 9.228 -0.564C9.228 -0.576 9.228 -0.66 9.252 -0.756L10.896 -7.32Z" id="gtexh5f3759df_39"/>
<path d="M1.952 -5.312C1.96 -5.328 1.984 -5.432 1.984 -5.44C1.984 -5.48 1.952 -5.552 1.856 -5.552C1.824 -5.552 1.576 -5.528 1.392 -5.512L0.944 -5.48C0.768 -5.464 0.688 -5.456 0.688 -5.312C0.688 -5.2 0.8 -5.2 0.896 -5.2C1.28 -5.2 1.28 -5.152 1.28 -5.08C1.28 -5.032 1.2 -4.712 1.152 -4.528L0.456 -1.744C0.392 -1.472 0.392 -1.352 0.392 -1.216C0.392 -0.392 0.896 0.08 1.512 0.08C2.496 0.08 3.52 -1.056 3.52 -2.216C3.52 -3.008 3.008 -3.528 2.368 -3.528C1.92 -3.528 1.576 -3.24 1.4 -3.088L1.952 -5.312ZM1.512 -0.144C1.224 -0.144 0.936 -0.368 0.936 -0.952C0.936 -1.168 0.968 -1.368 1.064 -1.752C1.12 -1.984 1.176 -2.208 1.24 -2.44C1.28 -2.584 1.28 -2.6 1.376 -2.72C1.648 -3.056 2.008 -3.304 2.344 -3.304C2.744 -3.304 2.896 -2.912 2.896 -2.552C2.896 -2.256 2.72 -1.4 2.48 -0.928C2.272 -0.496 1.888 -0.144 1.512 -0.144Z" id="gtexh5f3759df_40"/>
<path d="M1.6 -1.312C1.624 -1.432 1.704 -1.736 1.728 -1.856C1.84 -2.288 1.84 -2.296 2.024 -2.56C2.288 -2.952 2.664 -3.304 3.2 -3.304C3.488 -3.304 3.656 -3.136 3.656 -2.76C3.656 -2.32 3.32 -1.408 3.168 -1.016C3.064 -0.752 3.064 -0.704 3.064 -0.6C3.064 -0.144 3.44 0.08 3.784 0.08C4.568 0.08 4.896 -1.04 4.896 -1.144C4.896 -1.224 4.832 -1.248 4.776 -1.248C4.68 -1.248 4.664 -1.192 4.64 -1.112C4.448 -0.456 4.112 -0.144 3.808 -0.144C3.68 -0.144 3.616 -0.224 3.616 -0.408S3.68 -0.768 3.76 -0.968C3.88 -1.272 4.232 -2.192 4.232 -2.64C4.232 -3.24 3.816 -3.528 3.24 -3.528C2.592 -3.528 2.176 -3.136 1.944 -2.832C1.888 -3.272 1.536 -3.528 1.128 -3.528C0.84 -3.528 0.64 -3.344 0.512 -3.096C0.32 -2.72 0.24 -2.32 0.24 -2.304C0.24 -2.232 0.296 -2.2 0.36 -2.2C0.464 -2.2 0.472 -2.232 0.528 -2.44C0.624 -2.832 0.768 -3.304 1.104 -3.304C1.312 -3.304 1.36 -3.104 1.36 -2.928C1.36 -2.784 1.32 -2.632 1.256 -2.368C1.24 -2.304 1.12 -1.832 1.088 -1.72L0.792 -0.52C0.76 -0.4 0.712 -0.2 0.712 -0.168C0.712 0.016 0.864 0.08 0.968 0.08C1.112 0.08 1.232 -0.016 1.288 -0.112C1.312 -0.16 1.376 -0.432 1.416 -0.6L1.6 -1.312Z" id="gtexh5f3759df_41"/>
<path d="M3.156 1.344C2.832 1.8 2.364 2.208 1.776 2.208C1.632 2.208 1.056 2.184 0.876 1.632C0.912 1.644 0.972 1.644 0.996 1.644C1.356 1.644 1.596 1.332 1.596 1.056S1.368 0.684 1.188 0.684C0.996 0.684 0.576 0.828 0.576 1.416C0.576 2.028 1.092 2.448 1.776 2.448C2.976 2.448 4.188 1.344 4.524 0.012L5.7 -4.668C5.712 -4.728 5.736 -4.8 5.736 -4.872C5.736 -5.052 5.592 -5.172 5.412 -5.172C5.304 -5.172 5.052 -5.124 4.956 -4.764L4.068 -1.236C4.008 -1.02 4.008 -0.996 3.912 -0.864C3.672 -0.528 3.276 -0.12 2.7 -0.12C2.028 -0.12 1.968 -0.78 1.968 -1.104C1.968 -1.788 2.292 -2.712 2.616 -3.576C2.748 -3.924 2.82 -4.092 2.82 -4.332C2.82 -4.836 2.46 -5.292 1.872 -5.292C0.768 -5.292 0.324 -3.552 0.324 -3.456C0.324 -3.408 0.372 -3.348 0.456 -3.348C0.564 -3.348 0.576 -3.396 0.624 -3.564C0.912 -4.572 1.368 -5.052 1.836 -5.052C1.944 -5.052 2.148 -5.052 2.148 -4.656C2.148 -4.344 2.016 -3.996 1.836 -3.54C1.248 -1.968 1.248 -1.572 1.248 -1.284C1.248 -0.144 2.064 0.12 2.664 0.12C3.012 0.12 3.444 0.012 3.864 -0.432L3.876 -0.42C3.696 0.288 3.576 0.756 3.156 1.344Z" id="gtexh5f3759df_42"/>
<path d="M4.144 -3.016C4.176 -3.128 4.176 -3.144 4.176 -3.2C4.176 -3.4 4.016 -3.448 3.92 -3.448C3.88 -3.448 3.696 -3.44 3.592 -3.232C3.576 -3.192 3.504 -2.904 3.464 -2.736L2.984 -0.816C2.976 -0.792 2.632 -0.144 2.048 -0.144C1.656 -0.144 1.52 -0.432 1.52 -0.792C1.52 -1.256 1.792 -1.968 1.976 -2.432C2.056 -2.632 2.08 -2.704 2.08 -2.848C2.08 -3.288 1.728 -3.528 1.36 -3.528C0.568 -3.528 0.24 -2.4 0.24 -2.304C0.24 -2.232 0.296 -2.2 0.36 -2.2C0.464 -2.2 0.472 -2.248 0.496 -2.328C0.704 -3.024 1.048 -3.304 1.336 -3.304C1.456 -3.304 1.528 -3.224 1.528 -3.04C1.528 -2.872 1.464 -2.688 1.408 -2.544C1.08 -1.696 0.944 -1.288 0.944 -0.912C0.944 -0.128 1.536 0.08 2.008 0.08C2.384 0.08 2.656 -0.088 2.848 -0.272C2.736 0.176 2.656 0.488 2.352 0.872C2.088 1.2 1.768 1.408 1.408 1.408C1.272 1.408 0.968 1.384 0.808 1.144C1.232 1.112 1.264 0.752 1.264 0.704C1.264 0.512 1.12 0.408 0.952 0.408C0.776 0.408 0.496 0.544 0.496 0.936C0.496 1.312 0.84 1.632 1.408 1.632C2.224 1.632 3.144 0.976 3.384 0.008L4.144 -3.016Z" id="gtexh5f3759df_43"/>
<path d="M8.664 -5.424C8.664 -5.676 8.58 -5.784 8.496 -5.784C8.448 -5.784 8.34 -5.736 8.328 -5.46C8.28 -4.632 7.44 -4.14 6.66 -4.14C5.964 -4.14 5.364 -4.512 4.74 -4.932C4.092 -5.364 3.444 -5.796 2.664 -5.796C1.548 -5.796 0.66 -4.944 0.66 -3.84C0.66 -3.576 0.756 -3.48 0.828 -3.48C0.948 -3.48 0.996 -3.708 0.996 -3.756C1.056 -4.764 2.04 -5.124 2.664 -5.124C3.36 -5.124 3.96 -4.752 4.584 -4.332C5.232 -3.9 5.88 -3.468 6.66 -3.468C7.776 -3.468 8.664 -4.32 8.664 -5.424ZM8.664 -2.616C8.664 -2.964 8.52 -2.988 8.496 -2.988C8.448 -2.988 8.34 -2.928 8.328 -2.664C8.28 -1.836 7.44 -1.344 6.66 -1.344C5.964 -1.344 5.364 -1.716 4.74 -2.136C4.092 -2.568 3.444 -3 2.664 -3C1.548 -3 0.66 -2.148 0.66 -1.044C0.66 -0.78 0.756 -0.684 0.828 -0.684C0.948 -0.684 0.996 -0.912 0.996 -0.96C1.056 -1.968 2.04 -2.328 2.664 -2.328C3.36 -2.328 3.96 -1.956 4.584 -1.536C5.232 -1.104 5.88 -0.672 6.66 -0.672C7.8 -0.672 8.664 -1.56 8.664 -2.616Z" id="gtexh5f3759df_44"/>
<path d="M4.416 -7.38C4.524 -7.824 4.572 -7.848 5.04 -7.848H5.904C6.936 -7.848 7.704 -7.536 7.704 -6.6C7.704 -5.988 7.392 -4.224 4.98 -4.224H3.624L4.416 -7.38ZM6.084 -4.08C7.572 -4.404 8.736 -5.364 8.736 -6.396C8.736 -7.332 7.788 -8.196 6.12 -8.196H2.868C2.628 -8.196 2.52 -8.196 2.52 -7.968C2.52 -7.848 2.604 -7.848 2.832 -7.848C3.552 -7.848 3.552 -7.752 3.552 -7.62C3.552 -7.596 3.552 -7.524 3.504 -7.344L1.884 -0.888C1.776 -0.468 1.752 -0.348 0.924 -0.348C0.648 -0.348 0.564 -0.348 0.564 -0.12C0.564 0 0.696 0 0.732 0C0.948 0 1.2 -0.024 1.428 -0.024H2.844C3.06 -0.024 3.312 0 3.528 0C3.624 0 3.756 0 3.756 -0.228C3.756 -0.348 3.648 -0.348 3.468 -0.348C2.736 -0.348 2.736 -0.444 2.736 -0.564C2.736 -0.576 2.736 -0.66 2.76 -0.756L3.564 -3.984H5.004C6.144 -3.984 6.36 -3.264 6.36 -2.868C6.36 -2.688 6.24 -2.22 6.156 -1.908C6.024 -1.356 5.988 -1.224 5.988 -0.996C5.988 -0.144 6.684 0.252 7.488 0.252C8.46 0.252 8.88 -0.936 8.88 -1.104C8.88 -1.188 8.82 -1.224 8.748 -1.224C8.652 -1.224 8.628 -1.152 8.604 -1.056C8.316 -0.204 7.824 0.012 7.524 0.012S7.032 -0.12 7.032 -0.66C7.032 -0.948 7.176 -2.04 7.188 -2.1C7.248 -2.544 7.248 -2.592 7.248 -2.688C7.248 -3.564 6.54 -3.936 6.084 -4.08Z" id="gtexh5f3759df_45"/>
<path d="M2.024 -2.672C2.656 -2.672 3.056 -2.208 3.056 -1.368C3.056 -0.368 2.488 -0.072 2.064 -0.072C1.624 -0.072 1.024 -0.232 0.744 -0.656C1.032 -0.656 1.232 -0.84 1.232 -1.104C1.232 -1.36 1.048 -1.544 0.792 -1.544C0.576 -1.544 0.352 -1.408 0.352 -1.088C0.352 -0.328 1.168 0.168 2.08 0.168C3.144 0.168 3.888 -0.568 3.888 -1.368C3.888 -2.032 3.36 -2.64 2.544 -2.816C3.176 -3.04 3.648 -3.584 3.648 -4.224S2.928 -5.32 2.096 -5.32C1.24 -5.32 0.592 -4.856 0.592 -4.248C0.592 -3.952 0.792 -3.824 1 -3.824C1.248 -3.824 1.408 -4 1.408 -4.232C1.408 -4.528 1.152 -4.64 0.976 -4.648C1.312 -5.088 1.928 -5.112 2.072 -5.112C2.28 -5.112 2.888 -5.048 2.888 -4.224C2.888 -3.664 2.656 -3.328 2.544 -3.2C2.304 -2.952 2.12 -2.936 1.632 -2.904C1.48 -2.896 1.416 -2.888 1.416 -2.784C1.416 -2.672 1.488 -2.672 1.624 -2.672H2.024Z" id="gtexh5f3759df_46"/>
<path d="M3.264 10.56C3.108 12.528 2.676 13.068 1.992 13.068C1.836 13.068 1.476 13.032 1.224 12.816C1.572 12.768 1.668 12.492 1.668 12.324C1.668 11.976 1.404 11.82 1.176 11.82C0.936 11.82 0.672 11.976 0.672 12.336C0.672 12.912 1.272 13.332 1.992 13.332C3.132 13.332 3.708 12.288 3.972 11.208C4.128 10.584 4.56 7.092 4.656 5.76L4.884 2.772C5.052 0.564 5.46 0.264 6 0.264C6.12 0.264 6.492 0.288 6.756 0.516C6.408 0.564 6.312 0.84 6.312 1.008C6.312 1.356 6.576 1.512 6.804 1.512C7.044 1.512 7.308 1.356 7.308 0.996C7.308 0.42 6.708 0 5.988 0C4.848 0 4.38 1.164 4.176 2.076C4.032 2.736 3.6 6.12 3.492 7.572L3.264 10.56Z" id="gtexh5f3759df_47"/>
<path d="M3.488 -1.816H5.84C5.952 -1.816 6.128 -1.816 6.128 -2S5.952 -2.184 5.84 -2.184H3.488V-4.544C3.488 -4.656 3.488 -4.832 3.304 -4.832S3.12 -4.656 3.12 -4.544V-2.184H0.76C0.648 -2.184 0.472 -2.184 0.472 -2S0.648 -1.816 0.76 -1.816H3.12V0.544C3.12 0.656 3.12 0.832 3.304 0.832S3.488 0.656 3.488 0.544V-1.816Z" id="gtexh5f3759df_48"/>
<path d="M3.152 -5.176C3.152 -5.336 3.152 -5.4 2.984 -5.4C2.88 -5.4 2.872 -5.392 2.792 -5.28L0.24 -1.576V-1.312H2.496V-0.648C2.496 -0.352 2.472 -0.264 1.856 -0.264H1.672V0C2.352 -0.024 2.368 -0.024 2.824 -0.024S3.296 -0.024 3.976 0V-0.264H3.792C3.176 -0.264 3.152 -0.352 3.152 -0.648V-1.312H4V-1.576H3.152V-5.176ZM2.552 -4.528V-1.576H0.52L2.552 -4.528Z" id="gtexh5f3759df_49"/>
<path d="M2.656 -2.888C3.104 -3.104 3.648 -3.504 3.648 -4.128C3.648 -4.888 2.872 -5.32 2.128 -5.32C1.28 -5.32 0.592 -4.736 0.592 -3.984C0.592 -3.688 0.696 -3.416 0.896 -3.184C1.032 -3.016 1.064 -3 1.56 -2.688C0.568 -2.248 0.352 -1.664 0.352 -1.216C0.352 -0.336 1.24 0.168 2.112 0.168C3.096 0.168 3.888 -0.496 3.888 -1.344C3.888 -1.848 3.616 -2.184 3.488 -2.32C3.352 -2.448 3.344 -2.456 2.656 -2.888ZM1.416 -3.64C1.184 -3.776 0.992 -4.008 0.992 -4.288C0.992 -4.792 1.544 -5.112 2.112 -5.112C2.736 -5.112 3.248 -4.688 3.248 -4.128C3.248 -3.664 2.888 -3.272 2.416 -3.04L1.416 -3.64ZM1.808 -2.544C1.84 -2.528 2.752 -1.968 2.888 -1.88C3.016 -1.808 3.432 -1.552 3.432 -1.072C3.432 -0.456 2.784 -0.072 2.128 -0.072C1.416 -0.072 0.808 -0.56 0.808 -1.216C0.808 -1.816 1.256 -2.288 1.808 -2.544Z" id="gtexh5f3759df_50"/>
<path d="M1.384 -0.552C1.136 -0.576 0.936 -0.792 0.936 -1.08C0.936 -1.272 0.984 -1.344 1.04 -1.416C1.376 -1.216 1.688 -1.176 1.88 -1.176C2.672 -1.176 3.256 -1.728 3.256 -2.352C3.256 -2.656 3.12 -2.912 2.928 -3.12C3.04 -3.216 3.304 -3.384 3.696 -3.392C3.664 -3.368 3.624 -3.328 3.624 -3.2C3.624 -3.056 3.728 -2.952 3.872 -2.952C3.984 -2.952 4.12 -3.024 4.12 -3.208C4.12 -3.344 4.016 -3.616 3.664 -3.616C3.528 -3.616 3.144 -3.584 2.776 -3.248C2.576 -3.392 2.264 -3.528 1.888 -3.528C1.096 -3.528 0.512 -2.976 0.512 -2.352C0.512 -1.976 0.72 -1.696 0.888 -1.536C0.712 -1.336 0.648 -1.08 0.648 -0.888C0.648 -0.56 0.808 -0.312 0.984 -0.176C0.568 -0.064 0.24 0.248 0.24 0.616C0.24 1.192 1.064 1.64 2.12 1.64C3.12 1.64 4 1.232 4 0.6C4 0.168 3.744 -0.168 3.48 -0.304C3.008 -0.552 2.616 -0.552 1.832 -0.552H1.384ZM1.888 -1.408C1.144 -1.408 1.144 -2.176 1.144 -2.352C1.144 -2.648 1.184 -2.856 1.304 -3.016C1.44 -3.2 1.664 -3.296 1.88 -3.296C2.624 -3.296 2.624 -2.528 2.624 -2.352C2.624 -2.056 2.584 -1.848 2.464 -1.688C2.288 -1.464 2.032 -1.408 1.888 -1.408ZM2.264 -0.032C2.48 -0.032 3.528 -0.032 3.528 0.608C3.528 1.04 2.912 1.408 2.12 1.408C1.32 1.408 0.712 1.04 0.712 0.608C0.712 0.44 0.84 -0.032 1.496 -0.032H2.264Z" id="gtexh5f3759df_51"/>
<path d="M1.528 -5.552L0.336 -5.464V-5.2C0.88 -5.2 0.944 -5.144 0.944 -4.752V-0.624C0.944 -0.264 0.848 -0.264 0.336 -0.264V0C0.648 -0.024 1.096 -0.024 1.232 -0.024C1.392 -0.024 1.824 -0.024 2.136 0V-0.264C1.624 -0.264 1.528 -0.264 1.528 -0.624V-5.552Z" id="gtexh5f3759df_52"/>
<path d="M4 -1.704C4 -2.704 3.176 -3.568 2.12 -3.568S0.24 -2.704 0.24 -1.704S1.096 0.08 2.12 0.08C3.152 0.08 4 -0.704 4 -1.704ZM2.12 -0.168C1.688 -0.168 1.352 -0.376 1.176 -0.656C0.976 -0.984 0.952 -1.376 0.952 -1.776C0.952 -2.08 0.952 -2.56 1.2 -2.904C1.408 -3.184 1.744 -3.344 2.12 -3.344C2.536 -3.344 2.88 -3.144 3.064 -2.864C3.28 -2.528 3.288 -2.096 3.288 -1.776C3.288 -1.408 3.272 -0.968 3.048 -0.632C2.832 -0.312 2.472 -0.168 2.12 -0.168Z" id="gtexh5f3759df_53"/>
<path d="M2.208 -0.576C2.208 -0.924 1.92 -1.164 1.632 -1.164C1.284 -1.164 1.044 -0.876 1.044 -0.588C1.044 -0.24 1.332 0 1.62 0C1.968 0 2.208 -0.288 2.208 -0.576Z" id="gtexh5f3759df_54"/>
<path d="M5.376 -3.84C5.376 -4.836 5.316 -5.808 4.884 -6.72C4.392 -7.716 3.528 -7.98 2.94 -7.98C2.244 -7.98 1.392 -7.632 0.948 -6.636C0.612 -5.88 0.492 -5.136 0.492 -3.84C0.492 -2.676 0.576 -1.8 1.008 -0.948C1.476 -0.036 2.304 0.252 2.928 0.252C3.972 0.252 4.572 -0.372 4.92 -1.068C5.352 -1.968 5.376 -3.144 5.376 -3.84ZM2.928 0.012C2.544 0.012 1.764 -0.204 1.536 -1.512C1.404 -2.232 1.404 -3.144 1.404 -3.984C1.404 -4.968 1.404 -5.856 1.596 -6.564C1.8 -7.368 2.412 -7.74 2.928 -7.74C3.384 -7.74 4.08 -7.464 4.308 -6.432C4.464 -5.748 4.464 -4.8 4.464 -3.984C4.464 -3.18 4.464 -2.268 4.332 -1.536C4.104 -0.216 3.348 0.012 2.928 0.012Z" id="gtexh5f3759df_55"/>
<path d="M1.536 -6.876C2.052 -6.708 2.472 -6.696 2.604 -6.696C3.96 -6.696 4.824 -7.692 4.824 -7.86C4.824 -7.908 4.8 -7.968 4.728 -7.968C4.704 -7.968 4.68 -7.968 4.572 -7.92C3.9 -7.632 3.324 -7.596 3.012 -7.596C2.22 -7.596 1.656 -7.836 1.428 -7.932C1.344 -7.968 1.32 -7.968 1.308 -7.968C1.212 -7.968 1.212 -7.896 1.212 -7.704V-4.14C1.212 -3.924 1.212 -3.852 1.356 -3.852C1.416 -3.852 1.428 -3.864 1.548 -4.008C1.884 -4.5 2.448 -4.788 3.048 -4.788C3.684 -4.788 3.996 -4.2 4.092 -3.996C4.296 -3.528 4.308 -2.94 4.308 -2.484S4.308 -1.344 3.972 -0.804C3.708 -0.372 3.24 -0.072 2.712 -0.072C1.92 -0.072 1.14 -0.612 0.924 -1.488C0.984 -1.464 1.056 -1.452 1.116 -1.452C1.32 -1.452 1.644 -1.572 1.644 -1.98C1.644 -2.316 1.416 -2.508 1.116 -2.508C0.9 -2.508 0.588 -2.4 0.588 -1.932C0.588 -0.912 1.404 0.252 2.736 0.252C4.092 0.252 5.28 -0.888 5.28 -2.412C5.28 -3.84 4.32 -5.028 3.06 -5.028C2.376 -5.028 1.848 -4.728 1.536 -4.392V-6.876Z" id="gtexh5f3759df_56"/>
<path d="M2.664 2C2.728 2 2.824 2 2.824 1.904C2.824 1.872 2.816 1.864 2.712 1.76C1.616 0.728 1.344 -0.76 1.344 -2C1.344 -4.304 2.296 -5.384 2.704 -5.752C2.816 -5.856 2.824 -5.864 2.824 -5.904S2.792 -6 2.712 -6C2.584 -6 2.184 -5.592 2.12 -5.52C1.048 -4.4 0.824 -2.96 0.824 -2C0.824 -0.208 1.576 1.232 2.664 2Z" id="gtexh5f3759df_57"/>
<path d="M2.472 -2C2.472 -2.76 2.344 -3.672 1.848 -4.616C1.456 -5.352 0.728 -6 0.584 -6C0.504 -6 0.48 -5.944 0.48 -5.904C0.48 -5.872 0.48 -5.856 0.576 -5.76C1.696 -4.696 1.952 -3.232 1.952 -2C1.952 0.296 1 1.384 0.592 1.752C0.488 1.856 0.48 1.864 0.48 1.904S0.504 2 0.584 2C0.712 2 1.112 1.592 1.176 1.52C2.248 0.4 2.472 -1.04 2.472 -2Z" id="gtexh5f3759df_58"/>
<path d="M1.476 -4.176C1.476 -7.212 2.952 -7.692 3.6 -7.692C4.032 -7.692 4.464 -7.56 4.692 -7.2C4.548 -7.2 4.092 -7.2 4.092 -6.708C4.092 -6.444 4.272 -6.216 4.584 -6.216C4.884 -6.216 5.088 -6.396 5.088 -6.744C5.088 -7.368 4.632 -7.98 3.588 -7.98C2.076 -7.98 0.492 -6.432 0.492 -3.792C0.492 -0.492 1.932 0.252 2.952 0.252C4.26 0.252 5.376 -0.888 5.376 -2.448C5.376 -4.044 4.26 -5.112 3.06 -5.112C1.992 -5.112 1.596 -4.188 1.476 -3.852V-4.176ZM2.952 -0.072C2.196 -0.072 1.836 -0.744 1.728 -0.996C1.62 -1.308 1.5 -1.896 1.5 -2.736C1.5 -3.684 1.932 -4.872 3.012 -4.872C3.672 -4.872 4.02 -4.428 4.2 -4.02C4.392 -3.576 4.392 -2.976 4.392 -2.46C4.392 -1.848 4.392 -1.308 4.164 -0.852C3.864 -0.276 3.432 -0.072 2.952 -0.072Z" id="gtexh5f3759df_59"/></defs></svg><p>（注：本文包含数学公式，使用 SVG 图形格式，请不要在 RSS 阅读器里阅读本文）</p>
<p>基本上每个程序员都会对 Quake 那个神一样的常量 <code>0x5f3759df</code> 感到惊奇。在 Quake 里，有一段代码是计算光照时候使用的函数，计算 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 56.1197 14.2682 15.9603' height='15.9603pt' width='14.2682pt'style="line-height:1.330025em;vertical-align:-0.5066666666666669em;height:1.330025em;width:1.1890166666666666em">

<g>


<use x='67.6339' xlink:href='#gtexh5f3759df_4' y='61.2752'/>
<rect height='0.47998' width='11.8682' x='63.8248' y='62.76'/>
<use x='63.8248' xlink:href='#gtexh5f3759df_3' y='64.44'/>
<rect height='0.360001' width='4.78477' x='70.9082' y='64.08'/>
<use x='70.9082' xlink:href='#gtexh5f3759df_1' y='70.2072'/>
</g>
</svg>
，那段代码写的真叫经典：</p>
<pre class="mghl source cpp">float Q_rsqrt( float number ){
  long i;
  float x2, y;
  const float threehalfs = <span class="number">1.5</span>F;

  x2 = number * <span class="number">0.5</span>F;
  y  = number;
  i  = * ( long * ) &amp;y;  <span class="comment">// evil floating point bit level hacking</span>
  i  = <span class="number">0x5f3759df</span> - ( i >> <span class="number">1</span> ); <span class="comment">// WTF?</span>
  y  = * ( float * ) &amp;i;
  y  = y * ( threehalfs - ( x2 * y * y ) ); <span class="comment">// 1st iteration</span>
  <span class="comment">// y  = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed</span>

  return y
}</pre><p>任何学过数值分析的人肯定都知道，最后两行干的事情是牛顿迭代法，用迭代逼近算出准确值。整个过程里，先把单精度浮点 <code>number</code> “硬”转换成整数，进行一个非常诡异的减法后捣腾回实数，然后进行牛顿迭代。那个诡异的步骤显然是算出 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 56.1197 14.2682 15.9603' height='15.9603pt' width='14.2682pt'style="line-height:1.330025em;vertical-align:-0.5066666666666669em;height:1.330025em;width:1.1890166666666666em">

<g>


<use x='67.6339' xlink:href='#gtexh5f3759df_4' y='61.2752'/>
<rect height='0.47998' width='11.8682' x='63.8248' y='62.76'/>
<use x='63.8248' xlink:href='#gtexh5f3759df_3' y='64.44'/>
<rect height='0.360001' width='4.78477' x='70.9082' y='64.08'/>
<use x='70.9082' xlink:href='#gtexh5f3759df_1' y='70.2072'/>
</g>
</svg>
 的一个近似值，而且从只迭代了一次看，猜测的非常好。因为是计算光照用，千分之一的误差是可以允许的，否则，多迭代几行就是。</p>
<p>这肯定会引来无数人遐想的。不过你看过下面一段内容后就知道其中就里了。</p>
<p>根据 IEEE-754，每一个正的二进制浮点数都可以表示成 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 56.1786 90.078 12.8214' height='12.821400000000002pt' width='90.078pt'style="line-height:1.0684500000000001em;vertical-align:-0.25000000000000033em;height:1.0684500000000001em;width:7.5065em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_22' y='66'/>
<use x='72.6351' xlink:href='#gtexh5f3759df_9' y='66'/>
<use x='85.1072' xlink:href='#gtexh5f3759df_12' y='66'/>
<use x='89.6766' xlink:href='#gtexh5f3759df_6' y='66'/>
<use x='98.2182' xlink:href='#gtexh5f3759df_14' y='66'/>
<use x='110.024' xlink:href='#gtexh5f3759df_2' y='66'/>
<use x='120.301' xlink:href='#gtexh5f3759df_1' y='67.8'/>
<use x='125.586' xlink:href='#gtexh5f3759df_13' y='66'/>
<use x='130.155' xlink:href='#gtexh5f3759df_21' y='61.6453'/>
<use x='134.099' xlink:href='#gtexh5f3759df_19' y='62.6453'/>
<use x='138.786' xlink:href='#gtexh5f3759df_18' y='61.6453'/>
<use x='145.397' xlink:href='#gtexh5f3759df_20' y='61.6453'/>
</g>
</svg>
。其中，<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 60.8333 15.5624 6.96666' height='6.96666pt' width='15.5624pt'style="line-height:0.580555em;vertical-align:-0.14999666666666678em;height:0.580555em;width:1.2968666666666666em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_2' y='66'/>
<use x='72.9025' xlink:href='#gtexh5f3759df_1' y='67.8'/>
</g>
</svg>
 为其小数位，而 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 60.8333 10.7306 6.96666' height='6.96666pt' width='10.7306pt'style="line-height:0.580555em;vertical-align:-0.14999666666666678em;height:0.580555em;width:0.8942166666666668em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_35' y='66'/>
<use x='68.0706' xlink:href='#gtexh5f3759df_1' y='67.8'/>
</g>
</svg>
 是其指数部分。对于单精度来说，<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 60.8333 15.5624 6.96666' height='6.96666pt' width='15.5624pt'style="line-height:0.580555em;vertical-align:-0.14999666666666678em;height:0.580555em;width:1.2968666666666666em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_2' y='66'/>
<use x='72.9025' xlink:href='#gtexh5f3759df_1' y='67.8'/>
</g>
</svg>
 有 23 个二进制位，而 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 60.8333 10.7306 6.96666' height='6.96666pt' width='10.7306pt'style="line-height:0.580555em;vertical-align:-0.14999666666666678em;height:0.580555em;width:0.8942166666666668em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_35' y='66'/>
<use x='68.0706' xlink:href='#gtexh5f3759df_1' y='67.8'/>
</g>
</svg>
 则有 8 位，<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 57.8 42.962 8.2' height='8.2pt' width='42.962pt'style="line-height:0.6833333333333332em;vertical-align:2.960594732333751e-16em;height:0.6833333333333332em;width:3.580166666666667em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_5' y='66'/>
<use x='75.49' xlink:href='#gtexh5f3759df_9' y='66'/>
<use x='87.962' xlink:href='#gtexh5f3759df_6' y='66'/>
<use x='93.837' xlink:href='#gtexh5f3759df_7' y='66'/>
<use x='99.7119' xlink:href='#gtexh5f3759df_8' y='66'/>
</g>
</svg>
。</p>
<p>我们把它转换成“无符号整数形式”<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 57.8 79.3735 9.99998' height='9.99998pt' width='79.3735pt'style="line-height:0.8333316666666667em;vertical-align:-0.14999833333333315em;height:0.8333316666666667em;width:6.614458333333334em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_37' y='66'/>
<use x='67.8071' xlink:href='#gtexh5f3759df_1' y='67.8'/>
<use x='76.4251' xlink:href='#gtexh5f3759df_9' y='66'/>
<use x='88.8972' xlink:href='#gtexh5f3759df_36' y='66'/>
<use x='97.5951' xlink:href='#gtexh5f3759df_1' y='67.8'/>
<use x='102.88' xlink:href='#gtexh5f3759df_38' y='66'/>
<use x='113.541' xlink:href='#gtexh5f3759df_14' y='66'/>
<use x='125.346' xlink:href='#gtexh5f3759df_39' y='66'/>
<use x='136.714' xlink:href='#gtexh5f3759df_1' y='67.8'/>
</g>
</svg>
。对比两者可以得到：</p>
<div class="equation">
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='190.942 79.004 85.1602 71.7455' height='71.7455pt' width='85.1602pt'style="height:5.978791666666667em;width:7.096683333333334em">

<g>
<use x='190.942' xlink:href='#gtexh5f3759df_2' y='95.3181'/>
<use x='201.22' xlink:href='#gtexh5f3759df_1' y='97.1181'/>
<use x='216.505' xlink:href='#gtexh5f3759df_9' y='95.3181'/>
<use x='236.844' xlink:href='#gtexh5f3759df_39' y='87.2'/>
<use x='248.211' xlink:href='#gtexh5f3759df_1' y='89'/>
<rect height='0.47998' width='16.6521' x='236.844' y='92.0781'/>
<use x='241.172' xlink:href='#gtexh5f3759df_38' y='103.549'/>
<use x='195.774' xlink:href='#gtexh5f3759df_35' y='115.749'/>
<use x='201.22' xlink:href='#gtexh5f3759df_1' y='117.549'/>
<use x='216.505' xlink:href='#gtexh5f3759df_9' y='115.749'/>
<use x='235.644' xlink:href='#gtexh5f3759df_36' y='115.749'/>
<use x='244.341' xlink:href='#gtexh5f3759df_1' y='117.549'/>
<use x='252.293' xlink:href='#gtexh5f3759df_26' y='115.749'/>
<use x='264.293' xlink:href='#gtexh5f3759df_5' y='115.749'/>
<use x='198.51' xlink:href='#gtexh5f3759df_38' y='133.249'/>
<use x='216.505' xlink:href='#gtexh5f3759df_9' y='133.249'/>
<use x='235.644' xlink:href='#gtexh5f3759df_7' y='133.249'/>
<use x='241.519' xlink:href='#gtexh5f3759df_41' y='128.295'/>
<use x='246.676' xlink:href='#gtexh5f3759df_18' y='128.295'/>
<use x='253.287' xlink:href='#gtexh5f3759df_4' y='128.295'/>
<use x='257.537' xlink:href='#gtexh5f3759df_18' y='128.295'/>
<use x='264.149' xlink:href='#gtexh5f3759df_40' y='128.295'/>
<use x='196.973' xlink:href='#gtexh5f3759df_5' y='150.749'/>
<use x='216.505' xlink:href='#gtexh5f3759df_9' y='150.749'/>
<use x='235.644' xlink:href='#gtexh5f3759df_7' y='150.749'/>
<use x='241.519' xlink:href='#gtexh5f3759df_40' y='145.795'/>
<use x='245.155' xlink:href='#gtexh5f3759df_18' y='145.795'/>
<use x='251.766' xlink:href='#gtexh5f3759df_4' y='145.795'/>
<use x='259.183' xlink:href='#gtexh5f3759df_26' y='150.749'/>
<use x='271.183' xlink:href='#gtexh5f3759df_6' y='150.749'/>
</g>
</svg>
</div>
<p>那好，为了计算 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 56.1197 36.2332 15.9603' height='15.9603pt' width='36.2332pt'style="line-height:1.330025em;vertical-align:-0.5066666666666669em;height:1.330025em;width:3.019433333333333em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_42' y='66'/>
<use x='72.1178' xlink:href='#gtexh5f3759df_9' y='66'/>
<use x='89.5989' xlink:href='#gtexh5f3759df_4' y='61.2752'/>
<rect height='0.47998' width='11.8682' x='85.7898' y='62.76'/>
<use x='85.7898' xlink:href='#gtexh5f3759df_3' y='64.44'/>
<rect height='0.360001' width='4.78477' x='92.8733' y='64.08'/>
<use x='92.8733' xlink:href='#gtexh5f3759df_1' y='70.2072'/>
</g>
</svg>
，两边取对数，有：</p>
<div class="equation">
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='115.51 78.7533 236.095 52.4123' height='52.4123pt' width='236.095pt'style="height:4.3676916666666665em;width:19.674583333333334em">

<g>
<use x='169.87' xlink:href='#gtexh5f3759df_16' y='94.8514'/>
<use x='173.134' xlink:href='#gtexh5f3759df_17' y='94.8514'/>
<use x='179.009' xlink:href='#gtexh5f3759df_15' y='94.8514'/>
<use x='185.047' xlink:href='#gtexh5f3759df_11' y='97.6847'/>
<use x='189.797' xlink:href='#gtexh5f3759df_12' y='94.8514'/>
<use x='194.366' xlink:href='#gtexh5f3759df_42' y='94.8514'/>
<use x='200.526' xlink:href='#gtexh5f3759df_13' y='94.8514'/>
<use x='215.095' xlink:href='#gtexh5f3759df_9' y='94.8514'/>
<use x='234.234' xlink:href='#gtexh5f3759df_26' y='94.8514'/>
<use x='244.768' xlink:href='#gtexh5f3759df_6' y='86.7333'/>
<rect height='0.47998' width='5.87494' x='244.768' y='91.6114'/>
<use x='244.768' xlink:href='#gtexh5f3759df_7' y='103.083'/>
<use x='253.842' xlink:href='#gtexh5f3759df_16' y='94.8514'/>
<use x='257.106' xlink:href='#gtexh5f3759df_17' y='94.8514'/>
<use x='262.981' xlink:href='#gtexh5f3759df_15' y='94.8514'/>
<use x='269.019' xlink:href='#gtexh5f3759df_11' y='97.6847'/>
<use x='273.769' xlink:href='#gtexh5f3759df_12' y='94.8514'/>
<use x='278.339' xlink:href='#gtexh5f3759df_22' y='94.8514'/>
<use x='285.016' xlink:href='#gtexh5f3759df_13' y='94.8514'/>
<use x='115.51' xlink:href='#gtexh5f3759df_16' y='122.934'/>
<use x='118.774' xlink:href='#gtexh5f3759df_17' y='122.934'/>
<use x='124.649' xlink:href='#gtexh5f3759df_15' y='122.934'/>
<use x='130.687' xlink:href='#gtexh5f3759df_11' y='125.768'/>
<use x='135.437' xlink:href='#gtexh5f3759df_12' y='122.934'/>
<use x='140.007' xlink:href='#gtexh5f3759df_6' y='122.934'/>
<use x='148.548' xlink:href='#gtexh5f3759df_14' y='122.934'/>
<use x='160.354' xlink:href='#gtexh5f3759df_2' y='122.934'/>
<use x='170.631' xlink:href='#gtexh5f3759df_43' y='124.734'/>
<use x='175.62' xlink:href='#gtexh5f3759df_13' y='122.934'/>
<use x='182.856' xlink:href='#gtexh5f3759df_14' y='122.934'/>
<use x='194.661' xlink:href='#gtexh5f3759df_35' y='122.934'/>
<use x='200.107' xlink:href='#gtexh5f3759df_43' y='124.734'/>
<use x='215.095' xlink:href='#gtexh5f3759df_9' y='122.934'/>
<use x='234.234' xlink:href='#gtexh5f3759df_26' y='122.934'/>
<use x='244.768' xlink:href='#gtexh5f3759df_6' y='114.816'/>
<rect height='0.47998' width='5.87494' x='244.768' y='119.694'/>
<use x='244.768' xlink:href='#gtexh5f3759df_7' y='131.166'/>
<use x='253.842' xlink:href='#gtexh5f3759df_16' y='122.934'/>
<use x='257.106' xlink:href='#gtexh5f3759df_17' y='122.934'/>
<use x='262.981' xlink:href='#gtexh5f3759df_15' y='122.934'/>
<use x='269.019' xlink:href='#gtexh5f3759df_11' y='125.768'/>
<use x='273.769' xlink:href='#gtexh5f3759df_12' y='122.934'/>
<use x='278.339' xlink:href='#gtexh5f3759df_6' y='122.934'/>
<use x='286.88' xlink:href='#gtexh5f3759df_14' y='122.934'/>
<use x='298.686' xlink:href='#gtexh5f3759df_2' y='122.934'/>
<use x='308.964' xlink:href='#gtexh5f3759df_1' y='124.734'/>
<use x='314.248' xlink:href='#gtexh5f3759df_13' y='122.934'/>
<use x='321.484' xlink:href='#gtexh5f3759df_26' y='122.934'/>
<use x='334.684' xlink:href='#gtexh5f3759df_6' y='114.816'/>
<rect height='0.47998' width='5.87494' x='334.684' y='119.694'/>
<use x='334.684' xlink:href='#gtexh5f3759df_7' y='131.166'/>
<use x='341.759' xlink:href='#gtexh5f3759df_35' y='122.934'/>
<use x='347.205' xlink:href='#gtexh5f3759df_1' y='124.734'/>
</g>
</svg>
</div>
<p>为了去掉那个该死的对数，考虑到 在[0, 1] 上，<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 57 53.6558 12' height='12pt' width='53.6558pt'style="line-height:1em;vertical-align:-0.25em;height:1em;width:4.471316666666667em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_16' y='66'/>
<use x='65.8887' xlink:href='#gtexh5f3759df_17' y='66'/>
<use x='71.7636' xlink:href='#gtexh5f3759df_15' y='66'/>
<use x='77.8017' xlink:href='#gtexh5f3759df_11' y='68.8333'/>
<use x='82.5518' xlink:href='#gtexh5f3759df_12' y='66'/>
<use x='87.1212' xlink:href='#gtexh5f3759df_6' y='66'/>
<use x='95.6628' xlink:href='#gtexh5f3759df_14' y='66'/>
<use x='107.468' xlink:href='#gtexh5f3759df_10' y='66'/>
<use x='111.711' xlink:href='#gtexh5f3759df_13' y='66'/>
</g>
</svg>
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 58.619 16.9097 7.38097' height='7.38097pt' width='16.9097pt'style="line-height:0.6150808333333333em;vertical-align:0.0000025000000000533853em;height:0.6150808333333333em;width:1.4091416666666667em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_44' y='66'/>
<use x='75.2915' xlink:href='#gtexh5f3759df_10' y='66'/>
</g>
</svg>
，在这里引入一个常数 &sigma; 使得 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 57 53.6558 12' height='12pt' width='53.6558pt'style="line-height:1em;vertical-align:-0.25em;height:1em;width:4.471316666666667em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_16' y='66'/>
<use x='65.8887' xlink:href='#gtexh5f3759df_17' y='66'/>
<use x='71.7636' xlink:href='#gtexh5f3759df_15' y='66'/>
<use x='77.8017' xlink:href='#gtexh5f3759df_11' y='68.8333'/>
<use x='82.5518' xlink:href='#gtexh5f3759df_12' y='66'/>
<use x='87.1212' xlink:href='#gtexh5f3759df_6' y='66'/>
<use x='95.6628' xlink:href='#gtexh5f3759df_14' y='66'/>
<use x='107.468' xlink:href='#gtexh5f3759df_10' y='66'/>
<use x='111.711' xlink:href='#gtexh5f3759df_13' y='66'/>
</g>
</svg>
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 58.619 38.4907 8.29759' height='8.29759pt' width='38.4907pt'style="line-height:0.6914658333333333em;vertical-align:-0.07638249999999994em;height:0.6914658333333333em;width:3.207558333333333em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_44' y='66'/>
<use x='75.2915' xlink:href='#gtexh5f3759df_10' y='66'/>
<use x='82.2011' xlink:href='#gtexh5f3759df_14' y='66'/>
<use x='94.0065' xlink:href='#gtexh5f3759df_27' y='66'/>
</g>
</svg>
。于是：</p>
<div class="equation">
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='76.4312 78.7533 314.253 80.4952' height='80.4952pt' width='314.253pt'style="height:6.707933333333333em;width:26.187749999999998em">

<g>
<use x='100.523' xlink:href='#gtexh5f3759df_2' y='94.8514'/>
<use x='110.801' xlink:href='#gtexh5f3759df_43' y='96.6514'/>
<use x='118.456' xlink:href='#gtexh5f3759df_14' y='94.8514'/>
<use x='130.262' xlink:href='#gtexh5f3759df_27' y='94.8514'/>
<use x='140.037' xlink:href='#gtexh5f3759df_14' y='94.8514'/>
<use x='151.843' xlink:href='#gtexh5f3759df_35' y='94.8514'/>
<use x='157.288' xlink:href='#gtexh5f3759df_43' y='96.6514'/>
<use x='172.277' xlink:href='#gtexh5f3759df_9' y='94.8514'/>
<use x='191.416' xlink:href='#gtexh5f3759df_26' y='94.8514'/>
<use x='201.949' xlink:href='#gtexh5f3759df_6' y='86.7333'/>
<rect height='0.47998' width='5.87494' x='201.949' y='91.6114'/>
<use x='201.949' xlink:href='#gtexh5f3759df_7' y='103.083'/>
<use x='209.024' xlink:href='#gtexh5f3759df_12' y='94.8514'/>
<use x='213.593' xlink:href='#gtexh5f3759df_2' y='94.8514'/>
<use x='223.871' xlink:href='#gtexh5f3759df_1' y='96.6514'/>
<use x='231.822' xlink:href='#gtexh5f3759df_14' y='94.8514'/>
<use x='243.628' xlink:href='#gtexh5f3759df_27' y='94.8514'/>
<use x='253.403' xlink:href='#gtexh5f3759df_14' y='94.8514'/>
<use x='265.209' xlink:href='#gtexh5f3759df_35' y='94.8514'/>
<use x='270.655' xlink:href='#gtexh5f3759df_1' y='96.6514'/>
<use x='275.939' xlink:href='#gtexh5f3759df_13' y='94.8514'/>
<use x='76.4312' xlink:href='#gtexh5f3759df_39' y='122.934'/>
<use x='87.7984' xlink:href='#gtexh5f3759df_43' y='124.734'/>
<use x='95.4535' xlink:href='#gtexh5f3759df_14' y='122.934'/>
<use x='107.259' xlink:href='#gtexh5f3759df_12' y='122.934'/>
<use x='111.828' xlink:href='#gtexh5f3759df_36' y='122.934'/>
<use x='120.526' xlink:href='#gtexh5f3759df_43' y='124.734'/>
<use x='128.181' xlink:href='#gtexh5f3759df_26' y='122.934'/>
<use x='140.181' xlink:href='#gtexh5f3759df_5' y='122.934'/>
<use x='149.713' xlink:href='#gtexh5f3759df_13' y='122.934'/>
<use x='154.283' xlink:href='#gtexh5f3759df_38' y='122.934'/>
<use x='172.277' xlink:href='#gtexh5f3759df_9' y='122.934'/>
<use x='191.416' xlink:href='#gtexh5f3759df_26' y='122.934'/>
<use x='201.949' xlink:href='#gtexh5f3759df_29' y='114.816'/>
<rect height='0.47998' width='5.87494' x='201.949' y='119.694'/>
<use x='201.949' xlink:href='#gtexh5f3759df_7' y='131.166'/>
<use x='209.024' xlink:href='#gtexh5f3759df_27' y='122.934'/>
<use x='216.133' xlink:href='#gtexh5f3759df_38' y='122.934'/>
<use x='226.794' xlink:href='#gtexh5f3759df_26' y='122.934'/>
<use x='239.994' xlink:href='#gtexh5f3759df_6' y='114.816'/>
<rect height='0.47998' width='5.87494' x='239.994' y='119.694'/>
<use x='239.994' xlink:href='#gtexh5f3759df_7' y='131.166'/>
<use x='247.069' xlink:href='#gtexh5f3759df_39' y='122.934'/>
<use x='258.436' xlink:href='#gtexh5f3759df_1' y='124.734'/>
<use x='266.388' xlink:href='#gtexh5f3759df_26' y='122.934'/>
<use x='279.588' xlink:href='#gtexh5f3759df_6' y='114.816'/>
<rect height='0.47998' width='5.87494' x='279.588' y='119.694'/>
<use x='279.588' xlink:href='#gtexh5f3759df_7' y='131.166'/>
<use x='286.662' xlink:href='#gtexh5f3759df_12' y='122.934'/>
<use x='291.232' xlink:href='#gtexh5f3759df_36' y='122.934'/>
<use x='299.93' xlink:href='#gtexh5f3759df_1' y='124.734'/>
<use x='307.881' xlink:href='#gtexh5f3759df_26' y='122.934'/>
<use x='319.881' xlink:href='#gtexh5f3759df_5' y='122.934'/>
<use x='329.413' xlink:href='#gtexh5f3759df_13' y='122.934'/>
<use x='333.982' xlink:href='#gtexh5f3759df_38' y='122.934'/>
<use x='83.7923' xlink:href='#gtexh5f3759df_37' y='151.017'/>
<use x='88.9746' xlink:href='#gtexh5f3759df_43' y='152.817'/>
<use x='97.2964' xlink:href='#gtexh5f3759df_9' y='151.017'/>
<use x='109.768' xlink:href='#gtexh5f3759df_36' y='151.017'/>
<use x='118.466' xlink:href='#gtexh5f3759df_43' y='152.817'/>
<use x='123.455' xlink:href='#gtexh5f3759df_38' y='151.017'/>
<use x='134.116' xlink:href='#gtexh5f3759df_14' y='151.017'/>
<use x='145.921' xlink:href='#gtexh5f3759df_39' y='151.017'/>
<use x='157.288' xlink:href='#gtexh5f3759df_43' y='152.817'/>
<use x='172.277' xlink:href='#gtexh5f3759df_9' y='151.017'/>
<use x='192.616' xlink:href='#gtexh5f3759df_29' y='142.899'/>
<rect height='0.47998' width='5.87494' x='192.616' y='147.777'/>
<use x='192.616' xlink:href='#gtexh5f3759df_7' y='159.248'/>
<use x='199.691' xlink:href='#gtexh5f3759df_12' y='151.017'/>
<use x='204.26' xlink:href='#gtexh5f3759df_5' y='151.017'/>
<use x='216.459' xlink:href='#gtexh5f3759df_26' y='151.017'/>
<use x='228.459' xlink:href='#gtexh5f3759df_27' y='151.017'/>
<use x='235.567' xlink:href='#gtexh5f3759df_13' y='151.017'/>
<use x='240.137' xlink:href='#gtexh5f3759df_38' y='151.017'/>
<use x='250.798' xlink:href='#gtexh5f3759df_26' y='151.017'/>
<use x='263.998' xlink:href='#gtexh5f3759df_6' y='142.899'/>
<rect height='0.47998' width='5.87494' x='263.998' y='147.777'/>
<use x='263.998' xlink:href='#gtexh5f3759df_7' y='159.248'/>
<use x='271.073' xlink:href='#gtexh5f3759df_12' y='151.017'/>
<use x='275.642' xlink:href='#gtexh5f3759df_36' y='151.017'/>
<use x='284.34' xlink:href='#gtexh5f3759df_1' y='152.817'/>
<use x='289.625' xlink:href='#gtexh5f3759df_38' y='151.017'/>
<use x='300.286' xlink:href='#gtexh5f3759df_14' y='151.017'/>
<use x='312.091' xlink:href='#gtexh5f3759df_39' y='151.017'/>
<use x='323.459' xlink:href='#gtexh5f3759df_1' y='152.817'/>
<use x='328.743' xlink:href='#gtexh5f3759df_13' y='151.017'/>
<use x='336.646' xlink:href='#gtexh5f3759df_9' y='151.017'/>
<use x='349.118' xlink:href='#gtexh5f3759df_45' y='151.017'/>
<use x='360.827' xlink:href='#gtexh5f3759df_26' y='151.017'/>
<use x='374.027' xlink:href='#gtexh5f3759df_6' y='142.899'/>
<rect height='0.47998' width='5.87494' x='374.027' y='147.777'/>
<use x='374.027' xlink:href='#gtexh5f3759df_7' y='159.248'/>
<use x='381.102' xlink:href='#gtexh5f3759df_37' y='151.017'/>
<use x='386.284' xlink:href='#gtexh5f3759df_1' y='152.817'/>
</g>
</svg>
</div>
<p>这样就可以解释那一行里那个著名的 <code>0x5f3759df</code> 实际上就是 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 56.1197 79.9383 14.0184' height='14.0184pt' width='79.9383pt'style="line-height:1.1682em;vertical-align:-0.34484166666666677em;height:1.1682em;width:6.661525em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_45' y='66'/>
<use x='75.0004' xlink:href='#gtexh5f3759df_9' y='66'/>
<use x='88.6725' xlink:href='#gtexh5f3759df_46' y='61.2752'/>
<rect height='0.47998' width='4.25006' x='88.6725' y='62.76'/>
<use x='88.6725' xlink:href='#gtexh5f3759df_11' y='70.1381'/>
<use x='94.1225' xlink:href='#gtexh5f3759df_12' y='66'/>
<use x='98.6919' xlink:href='#gtexh5f3759df_5' y='66'/>
<use x='110.89' xlink:href='#gtexh5f3759df_26' y='66'/>
<use x='122.89' xlink:href='#gtexh5f3759df_27' y='66'/>
<use x='129.999' xlink:href='#gtexh5f3759df_13' y='66'/>
<use x='134.569' xlink:href='#gtexh5f3759df_38' y='66'/>
</g>
</svg>
。取 &sigma; = 0 代入尝试，可得到 R = 1598029824 = <code>0x5f400000</code>。这个数值已经比较接近了，然而还不准确，毕竟这里 &sigma; = 0。</p>
<p>那么 &sigma; 设置成多少合适呢？由于希望用牛顿法计算，自然需要让 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 57 53.6558 12' height='12pt' width='53.6558pt'style="line-height:1em;vertical-align:-0.25em;height:1em;width:4.471316666666667em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_16' y='66'/>
<use x='65.8887' xlink:href='#gtexh5f3759df_17' y='66'/>
<use x='71.7636' xlink:href='#gtexh5f3759df_15' y='66'/>
<use x='77.8017' xlink:href='#gtexh5f3759df_11' y='68.8333'/>
<use x='82.5518' xlink:href='#gtexh5f3759df_12' y='66'/>
<use x='87.1212' xlink:href='#gtexh5f3759df_6' y='66'/>
<use x='95.6628' xlink:href='#gtexh5f3759df_14' y='66'/>
<use x='107.468' xlink:href='#gtexh5f3759df_10' y='66'/>
<use x='111.711' xlink:href='#gtexh5f3759df_13' y='66'/>
</g>
</svg>
 和 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 58.619 25.824 8.29759' height='8.29759pt' width='25.824pt'style="line-height:0.6914658333333333em;vertical-align:-0.07638249999999994em;height:0.6914658333333333em;width:2.152em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_10' y='66'/>
<use x='69.5345' xlink:href='#gtexh5f3759df_14' y='66'/>
<use x='81.3399' xlink:href='#gtexh5f3759df_27' y='66'/>
</g>
</svg>
 差异尽量小。我使用的是最小二乘法，即计算积分：</p>
<div class="equation">
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='39 78.8356 378.263 30.5009' height='30.5009pt' width='378.263pt'style="height:2.541741666666667em;width:31.521916666666666em">

<g>
<use x='39' xlink:href='#gtexh5f3759df_25' y='80.9889'/>
<use x='51' xlink:href='#gtexh5f3759df_4' y='84.1556'/>
<use x='45.6667' xlink:href='#gtexh5f3759df_28' y='108.156'/>
<use x='57.7501' xlink:href='#gtexh5f3759df_32' y='97.3224'/>
<use x='61.0139' xlink:href='#gtexh5f3759df_16' y='97.3224'/>
<use x='64.2778' xlink:href='#gtexh5f3759df_17' y='97.3224'/>
<use x='70.1527' xlink:href='#gtexh5f3759df_15' y='97.3224'/>
<use x='76.1908' xlink:href='#gtexh5f3759df_11' y='100.156'/>
<use x='80.9409' xlink:href='#gtexh5f3759df_12' y='97.3224'/>
<use x='85.5103' xlink:href='#gtexh5f3759df_6' y='97.3224'/>
<use x='94.0519' xlink:href='#gtexh5f3759df_14' y='97.3224'/>
<use x='105.857' xlink:href='#gtexh5f3759df_10' y='97.3224'/>
<use x='110.1' xlink:href='#gtexh5f3759df_13' y='97.3224'/>
<use x='117.336' xlink:href='#gtexh5f3759df_26' y='97.3224'/>
<use x='129.336' xlink:href='#gtexh5f3759df_12' y='97.3224'/>
<use x='133.906' xlink:href='#gtexh5f3759df_10' y='97.3224'/>
<use x='140.815' xlink:href='#gtexh5f3759df_14' y='97.3224'/>
<use x='152.621' xlink:href='#gtexh5f3759df_27' y='97.3224'/>
<use x='159.73' xlink:href='#gtexh5f3759df_13' y='97.3224'/>
<use x='164.299' xlink:href='#gtexh5f3759df_33' y='97.3224'/>
<use x='167.563' xlink:href='#gtexh5f3759df_11' y='91.489'/>
<use x='174.313' xlink:href='#gtexh5f3759df_34' y='97.3224'/>
<use x='180.841' xlink:href='#gtexh5f3759df_10' y='97.3224'/>
<use x='188.417' xlink:href='#gtexh5f3759df_9' y='97.3224'/>
<use x='200.889' xlink:href='#gtexh5f3759df_10' y='97.3224'/>
<use x='207.132' xlink:href='#gtexh5f3759df_23' y='80.4022'/>
<use x='215.965' xlink:href='#gtexh5f3759df_10' y='97.3224'/>
<use x='222.875' xlink:href='#gtexh5f3759df_26' y='97.3224'/>
<use x='234.875' xlink:href='#gtexh5f3759df_29' y='97.3224'/>
<use x='243.417' xlink:href='#gtexh5f3759df_14' y='97.3224'/>
<use x='268.58' xlink:href='#gtexh5f3759df_7' y='89.2043'/>
<rect height='0.47998' width='30.1907' x='256.422' y='94.0824'/>
<use x='256.422' xlink:href='#gtexh5f3759df_16' y='105.554'/>
<use x='259.686' xlink:href='#gtexh5f3759df_17' y='105.554'/>
<use x='265.561' xlink:href='#gtexh5f3759df_15' y='105.554'/>
<use x='271.599' xlink:href='#gtexh5f3759df_12' y='105.554'/>
<use x='276.168' xlink:href='#gtexh5f3759df_7' y='105.554'/>
<use x='282.043' xlink:href='#gtexh5f3759df_13' y='105.554'/>
<use x='287.813' xlink:href='#gtexh5f3759df_24' y='80.4022'/>
<use x='299.313' xlink:href='#gtexh5f3759df_14' y='97.3224'/>
<use x='312.318' xlink:href='#gtexh5f3759df_8' y='89.2043'/>
<rect height='0.47998' width='5.87494' x='312.318' y='94.0824'/>
<use x='312.318' xlink:href='#gtexh5f3759df_29' y='105.554'/>
<use x='322.06' xlink:href='#gtexh5f3759df_14' y='97.3224'/>
<use x='349.598' xlink:href='#gtexh5f3759df_7' y='89.2043'/>
<rect height='0.47998' width='34.9407' x='335.065' y='94.0824'/>
<use x='335.065' xlink:href='#gtexh5f3759df_16' y='106.324'/>
<use x='338.329' xlink:href='#gtexh5f3759df_17' y='106.324'/>
<use x='344.204' xlink:href='#gtexh5f3759df_15' y='106.324'/>
<use x='350.242' xlink:href='#gtexh5f3759df_11' y='101.158'/>
<use x='354.992' xlink:href='#gtexh5f3759df_12' y='106.324'/>
<use x='359.561' xlink:href='#gtexh5f3759df_7' y='106.324'/>
<use x='365.436' xlink:href='#gtexh5f3759df_13' y='106.324'/>
<use x='373.872' xlink:href='#gtexh5f3759df_26' y='97.3224'/>
<use x='399.23' xlink:href='#gtexh5f3759df_31' y='89.2043'/>
<rect height='0.47998' width='30.1907' x='387.072' y='94.0824'/>
<use x='387.072' xlink:href='#gtexh5f3759df_16' y='105.554'/>
<use x='390.336' xlink:href='#gtexh5f3759df_17' y='105.554'/>
<use x='396.211' xlink:href='#gtexh5f3759df_15' y='105.554'/>
<use x='402.249' xlink:href='#gtexh5f3759df_12' y='105.554'/>
<use x='406.819' xlink:href='#gtexh5f3759df_30' y='105.554'/>
<use x='412.694' xlink:href='#gtexh5f3759df_13' y='105.554'/>
</g>
</svg>
</div>
<p>积分 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 54.3444 142.084 15.8224' height='15.8224pt' width='142.084pt'style="line-height:1.3185333333333333em;vertical-align:-0.3472333333333334em;height:1.3185333333333333em;width:11.840333333333334em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_47' y='56.3333'/>
<use x='70.6248' xlink:href='#gtexh5f3759df_4' y='59.4999'/>
<use x='68.2915' xlink:href='#gtexh5f3759df_28' y='70.1667'/>
<use x='77.3749' xlink:href='#gtexh5f3759df_32' y='66'/>
<use x='80.6387' xlink:href='#gtexh5f3759df_16' y='66'/>
<use x='83.9026' xlink:href='#gtexh5f3759df_17' y='66'/>
<use x='89.7775' xlink:href='#gtexh5f3759df_15' y='66'/>
<use x='95.8156' xlink:href='#gtexh5f3759df_11' y='68.8333'/>
<use x='100.566' xlink:href='#gtexh5f3759df_12' y='66'/>
<use x='105.135' xlink:href='#gtexh5f3759df_6' y='66'/>
<use x='113.677' xlink:href='#gtexh5f3759df_14' y='66'/>
<use x='125.482' xlink:href='#gtexh5f3759df_10' y='66'/>
<use x='129.725' xlink:href='#gtexh5f3759df_13' y='66'/>
<use x='136.961' xlink:href='#gtexh5f3759df_26' y='66'/>
<use x='148.961' xlink:href='#gtexh5f3759df_12' y='66'/>
<use x='153.531' xlink:href='#gtexh5f3759df_10' y='66'/>
<use x='160.44' xlink:href='#gtexh5f3759df_14' y='66'/>
<use x='172.246' xlink:href='#gtexh5f3759df_27' y='66'/>
<use x='179.355' xlink:href='#gtexh5f3759df_13' y='66'/>
<use x='183.924' xlink:href='#gtexh5f3759df_33' y='66'/>
<use x='187.188' xlink:href='#gtexh5f3759df_11' y='60.1667'/>
<use x='193.938' xlink:href='#gtexh5f3759df_34' y='66'/>
<use x='200.466' xlink:href='#gtexh5f3759df_10' y='66'/>
</g>
</svg>
 的结果是 &sigma; 的二次函数，可算出其最小值时，&sigma; 为 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 55.1689 36.5185 16.5247' height='16.5247pt' width='36.5185pt'style="line-height:1.3770583333333333em;vertical-align:-0.47446666666666665em;height:1.3770583333333333em;width:3.0432083333333337em">

<g>


<use x='63.8248' xlink:href='#gtexh5f3759df_18' y='60.7245'/>
<use x='70.436' xlink:href='#gtexh5f3759df_11' y='60.7245'/>
<use x='74.6861' xlink:href='#gtexh5f3759df_48' y='60.7245'/>
<use x='81.2973' xlink:href='#gtexh5f3759df_52' y='60.7245'/>
<use x='83.6584' xlink:href='#gtexh5f3759df_53' y='60.7245'/>
<use x='87.9085' xlink:href='#gtexh5f3759df_51' y='60.7245'/>
<use x='93.6933' xlink:href='#gtexh5f3759df_50' y='60.7245'/>
<rect height='0.47998' width='34.1185' x='63.8248' y='62.76'/>
<use x='72.5611' xlink:href='#gtexh5f3759df_52' y='70.1381'/>
<use x='74.9222' xlink:href='#gtexh5f3759df_53' y='70.1381'/>
<use x='79.1723' xlink:href='#gtexh5f3759df_51' y='70.1381'/>
<use x='84.9571' xlink:href='#gtexh5f3759df_49' y='70.1381'/>
</g>
</svg>
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 58.2667 39.4303 7.73332' height='7.73332pt' width='39.4303pt'style="line-height:0.6444433333333334em;vertical-align:-0.0000016666666666775851em;height:0.6444433333333334em;width:3.2858583333333335em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_44' y='66'/>
<use x='75.2915' xlink:href='#gtexh5f3759df_55' y='66'/>
<use x='81.1664' xlink:href='#gtexh5f3759df_54' y='66'/>
<use x='84.4303' xlink:href='#gtexh5f3759df_55' y='66'/>
<use x='90.3052' xlink:href='#gtexh5f3759df_56' y='66'/>
<use x='96.1801' xlink:href='#gtexh5f3759df_8' y='66'/>
</g>
</svg>
。利用这个 &sigma; 计算出的 R = <code>0x5f34ff58</code>。Quake 里使用的 &sigma; = 0.0450461875791687011756，比用最小二乘法算出的 &sigma; 小。另一个可以选用的 &sigma; 是 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 57 74.1973 12' height='12pt' width='74.1973pt'style="line-height:1em;vertical-align:-0.25em;height:1em;width:6.183108333333333em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_16' y='66'/>
<use x='65.8887' xlink:href='#gtexh5f3759df_17' y='66'/>
<use x='71.7636' xlink:href='#gtexh5f3759df_15' y='66'/>
<use x='77.8017' xlink:href='#gtexh5f3759df_11' y='68.8333'/>
<use x='82.5518' xlink:href='#gtexh5f3759df_12' y='66'/>
<use x='87.1212' xlink:href='#gtexh5f3759df_6' y='66'/>
<use x='95.6628' xlink:href='#gtexh5f3759df_14' y='66'/>
<use x='107.468' xlink:href='#gtexh5f3759df_10' y='66'/>
<use x='111.711' xlink:href='#gtexh5f3759df_13' y='66'/>
<use x='118.947' xlink:href='#gtexh5f3759df_26' y='66'/>
<use x='130.947' xlink:href='#gtexh5f3759df_6' y='66'/>
</g>
</svg>
 在 [0, 1] 上的最大值的一半，为 <svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 54.28 81.1442 17.8581' height='17.8581pt' width='81.1442pt'style="line-height:1.488175em;vertical-align:-0.5115083333333335em;height:1.488175em;width:6.762016666666667em">

<g>


<use x='63.8248' xlink:href='#gtexh5f3759df_52' y='60.28'/>
<use x='66.186' xlink:href='#gtexh5f3759df_53' y='60.28'/>
<use x='70.436' xlink:href='#gtexh5f3759df_51' y='60.28'/>
<use x='74.8041' xlink:href='#gtexh5f3759df_57' y='60.28'/>
<use x='78.1097' xlink:href='#gtexh5f3759df_11' y='60.28'/>
<use x='82.3598' xlink:href='#gtexh5f3759df_58' y='60.28'/>
<use x='85.6654' xlink:href='#gtexh5f3759df_18' y='60.28'/>
<use x='92.2766' xlink:href='#gtexh5f3759df_52' y='60.28'/>
<use x='94.6378' xlink:href='#gtexh5f3759df_53' y='60.28'/>
<use x='98.8878' xlink:href='#gtexh5f3759df_51' y='60.28'/>
<use x='103.256' xlink:href='#gtexh5f3759df_57' y='60.28'/>
<use x='106.562' xlink:href='#gtexh5f3759df_52' y='60.28'/>
<use x='108.923' xlink:href='#gtexh5f3759df_53' y='60.28'/>
<use x='113.173' xlink:href='#gtexh5f3759df_51' y='60.28'/>
<use x='117.541' xlink:href='#gtexh5f3759df_57' y='60.28'/>
<use x='120.846' xlink:href='#gtexh5f3759df_11' y='60.28'/>
<use x='125.097' xlink:href='#gtexh5f3759df_58' y='60.28'/>
<use x='128.402' xlink:href='#gtexh5f3759df_58' y='60.28'/>
<use x='131.708' xlink:href='#gtexh5f3759df_18' y='60.28'/>
<use x='138.319' xlink:href='#gtexh5f3759df_4' y='60.28'/>
<rect height='0.47998' width='78.7442' x='63.8248' y='62.76'/>
<use x='89.4432' xlink:href='#gtexh5f3759df_11' y='70.1381'/>
<use x='95.11' xlink:href='#gtexh5f3759df_52' y='70.1381'/>
<use x='97.4711' xlink:href='#gtexh5f3759df_53' y='70.1381'/>
<use x='101.721' xlink:href='#gtexh5f3759df_51' y='70.1381'/>
<use x='106.089' xlink:href='#gtexh5f3759df_57' y='70.1381'/>
<use x='109.395' xlink:href='#gtexh5f3759df_11' y='70.1381'/>
<use x='113.645' xlink:href='#gtexh5f3759df_58' y='70.1381'/>
</g>
</svg>
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 58.2667 57.0551 7.73332' height='7.733319999999999pt' width='57.0551pt'style="line-height:0.6444433333333333em;vertical-align:-0.0000016666666666775851em;height:0.6444433333333333em;width:4.754591666666667em">

<g>


<use x='62.6248' xlink:href='#gtexh5f3759df_44' y='66'/>
<use x='75.2915' xlink:href='#gtexh5f3759df_55' y='66'/>
<use x='81.1664' xlink:href='#gtexh5f3759df_54' y='66'/>
<use x='84.4303' xlink:href='#gtexh5f3759df_55' y='66'/>
<use x='90.3052' xlink:href='#gtexh5f3759df_30' y='66'/>
<use x='96.1801' xlink:href='#gtexh5f3759df_29' y='66'/>
<use x='102.055' xlink:href='#gtexh5f3759df_55' y='66'/>
<use x='107.93' xlink:href='#gtexh5f3759df_29' y='66'/>
<use x='113.805' xlink:href='#gtexh5f3759df_59' y='66'/>
</g>
</svg>
，算出的 R 为 1597488340 = <code>0x4f37bcb6</code>。</p>
<p>当然了，用最小二乘法去“猜” &sigma; 并不是非常好，最好的 &sigma; 肯定是用大量实验选择的，<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='62.6248 55.1689 36.5185 16.5247' height='16.5247pt' width='36.5185pt'style="line-height:1.3770583333333333em;vertical-align:-0.47446666666666665em;height:1.3770583333333333em;width:3.0432083333333337em">

<g>


<use x='63.8248' xlink:href='#gtexh5f3759df_18' y='60.7245'/>
<use x='70.436' xlink:href='#gtexh5f3759df_11' y='60.7245'/>
<use x='74.6861' xlink:href='#gtexh5f3759df_48' y='60.7245'/>
<use x='81.2973' xlink:href='#gtexh5f3759df_52' y='60.7245'/>
<use x='83.6584' xlink:href='#gtexh5f3759df_53' y='60.7245'/>
<use x='87.9085' xlink:href='#gtexh5f3759df_51' y='60.7245'/>
<use x='93.6933' xlink:href='#gtexh5f3759df_50' y='60.7245'/>
<rect height='0.47998' width='34.1185' x='63.8248' y='62.76'/>
<use x='72.5611' xlink:href='#gtexh5f3759df_52' y='70.1381'/>
<use x='74.9222' xlink:href='#gtexh5f3759df_53' y='70.1381'/>
<use x='79.1723' xlink:href='#gtexh5f3759df_51' y='70.1381'/>
<use x='84.9571' xlink:href='#gtexh5f3759df_49' y='70.1381'/>
</g>
</svg>
 这个 &sigma; 值并不是非常好的值，但是作为一次尝试，已经是很不错了。</p>]]></content:encoded>
			<wfw:commentRss>http://typeof.net/2011/11/how-0x5f3759df-calculated/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>你喜欢这样定义类吗？这就是 eisa 的 def</title>
		<link>http://typeof.net/2011/10/define-types-in-eisas-def/</link>
		<comments>http://typeof.net/2011/10/define-types-in-eisas-def/#comments</comments>
		<pubDate>Thu, 27 Oct 2011 13:38:58 +0000</pubDate>
		<dc:creator>Belleve Invis</dc:creator>
				<category><![CDATA[Discussion]]></category>
		<category><![CDATA[Original]]></category>
		<category><![CDATA[中文]]></category>
		<category><![CDATA[def]]></category>
		<category><![CDATA[eisa]]></category>
		<category><![CDATA[mine]]></category>
		<category><![CDATA[syntax]]></category>

		<guid isPermaLink="false">http://typeof.net/?p=358</guid>
		<description><![CDATA[// Also: Man = type(function(name)...) def type Man(name = "Unnamed"): @name = name end def Man.prototype.say(something): tracel @name + ': ' + something end var tom = Man.new 'Tom'; tom.say 'Hello' // Also: Priate = outof(Man)(function(name, spname)...) def outof(Man) Priate(name = "Unnamed", spname = "Unknown"): Man.call this, name @spname = spname end var beard = [...]]]></description>
			<content:encoded><![CDATA[<pre class="mghl source lofn"><span class="comment">// Also: Man = type(function(name)...)</span>
<span class="keyword">def</span> <span class="identifier">type</span> <span class="identifier">Man</span><span class="punctor">(</span><span class="identifier">name</span> <span class="operator">=</span> <span class="string literal">"Unnamed"</span><span class="punctor">)</span><span class="punctor">:</span>
    <span class="operator">@</span><span class="identifier">name</span> <span class="operator">=</span> <span class="identifier">name</span>
<span class="keyword">end</span>
<span class="keyword">def</span> <span class="identifier">Man</span><span class="punctor">.</span><span class="identifier">prototype</span><span class="punctor">.</span><span class="identifier">say</span><span class="punctor">(</span><span class="identifier">something</span><span class="punctor">)</span><span class="punctor">:</span>
    <span class="identifier">tracel</span> <span class="operator">@</span><span class="identifier">name</span> <span class="operator">+</span> <span class="string literal">': '</span> <span class="operator">+</span> <span class="identifier">something</span>
<span class="keyword">end</span>
<span class="keyword">var</span> <span class="identifier">tom</span> <span class="operator">=</span> <span class="identifier">Man</span><span class="punctor">.</span><span class="identifier">new</span> <span class="string literal">'Tom'</span><span class="punctor">;</span>
<span class="identifier">tom</span><span class="punctor">.</span><span class="identifier">say</span> <span class="string literal">'Hello'</span>
<span class="comment">// Also: Priate = outof(Man)(function(name, spname)...)</span>
<span class="keyword">def</span> <span class="identifier">outof</span><span class="punctor">(</span><span class="identifier">Man</span><span class="punctor">)</span> <span class="identifier">Priate</span><span class="punctor">(</span><span class="identifier">name</span> <span class="operator">=</span> <span class="string literal">"Unnamed"</span><span class="punctor">,</span> <span class="identifier">spname</span> <span class="operator">=</span> <span class="string literal">"Unknown"</span><span class="punctor">)</span><span class="punctor">:</span>
    <span class="identifier">Man</span><span class="punctor">.</span><span class="identifier">call</span> <span class="keyword">this</span><span class="punctor">,</span> <span class="identifier">name</span>
    <span class="operator">@</span><span class="identifier">spname</span> <span class="operator">=</span> <span class="identifier">spname</span>
<span class="keyword">end</span>
<span class="keyword">var</span> <span class="identifier">beard</span> <span class="operator">=</span> <span class="identifier">Priate</span><span class="punctor">.</span><span class="identifier">new</span> <span class="string literal">'Peter'</span><span class="punctor">,</span> <span class="string literal">'Blue Beard'</span>
<span class="identifier">tracel</span> <span class="identifier">beard</span><span class="punctor">.</span><span class="identifier">spname</span>
<span class="identifier">beard</span><span class="punctor">.</span><span class="identifier">say</span> <span class="string literal">'Yarr!'</span></pre>]]></content:encoded>
			<wfw:commentRss>http://typeof.net/2011/10/define-types-in-eisas-def/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>行数 17259</title>
		<link>http://typeof.net/2011/10/lines-17259/</link>
		<comments>http://typeof.net/2011/10/lines-17259/#comments</comments>
		<pubDate>Tue, 11 Oct 2011 07:47:40 +0000</pubDate>
		<dc:creator>Belleve Invis</dc:creator>
				<category><![CDATA[Browser]]></category>
		<category><![CDATA[Critique]]></category>
		<category><![CDATA[中文]]></category>
		<category><![CDATA[compiler]]></category>
		<category><![CDATA[dart]]></category>
		<category><![CDATA[joke]]></category>

		<guid isPermaLink="false">http://typeof.net/?p=354</guid>
		<description><![CDATA[http://gist.github.com/1277224 从 8 行到 17259 行。从一个侧面也证明了把静态语言“编译”成动态语言有多困难。Dart 注定是个笑话。 ps. 今天 Eisa 的总行数只有 4643 行。]]></description>
			<content:encoded><![CDATA[
<p>http://gist.github.com/1277224</p>
<p>从 8 行到 17259 行。从一个侧面也证明了把静态语言“编译”成动态语言有多困难。Dart 注定是个笑话。</p>
<p>ps. 今天 Eisa 的总行数只有 4643 行。</p>
]]></content:encoded>
			<wfw:commentRss>http://typeof.net/2011/10/lines-17259/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>自己动手开发编译器（补完）</title>
		<link>http://typeof.net/2011/10/compiler-by-myself-fina/</link>
		<comments>http://typeof.net/2011/10/compiler-by-myself-fina/#comments</comments>
		<pubDate>Sun, 09 Oct 2011 07:41:06 +0000</pubDate>
		<dc:creator>Belleve Invis</dc:creator>
				<category><![CDATA[Discussion]]></category>
		<category><![CDATA[dotNET/mono]]></category>
		<category><![CDATA[中文]]></category>
		<category><![CDATA[~ninputer]]></category>

		<guid isPermaLink="false">http://typeof.net/?p=347</guid>
		<description><![CDATA[第十一：http://www.cnblogs.com/Ninputer/archive/2011/07/22/2112030.html 第十二：http://www.cnblogs.com/Ninputer/archive/2011/08/02/2120435.html 没想到作者自己坑掉了 0_0那我也跟着坑掉好了（本来还想看看后端技术地说）]]></description>
			<content:encoded><![CDATA[<p>第十一：<a href="http://www.cnblogs.com/Ninputer/archive/2011/07/22/2112030.html">http://www.cnblogs.com/Ninputer/archive/2011/07/22/2112030.html</a></p>
<p>第十二：<a href="http://www.cnblogs.com/Ninputer/archive/2011/08/02/2120435.html">http://www.cnblogs.com/Ninputer/archive/2011/08/02/2120435.html</a></p>
<p>没想到作者自己坑掉了 0_0<br />那我也跟着坑掉好了（本来还想看看后端技术地说）</p>]]></content:encoded>
			<wfw:commentRss>http://typeof.net/2011/10/compiler-by-myself-fina/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>自己动手开发编译器（十）：miniSharp语法分析器</title>
		<link>http://typeof.net/2011/07/compiler-by-myself-10/</link>
		<comments>http://typeof.net/2011/07/compiler-by-myself-10/#comments</comments>
		<pubDate>Mon, 18 Jul 2011 13:06:02 +0000</pubDate>
		<dc:creator>Belleve Invis</dc:creator>
				<category><![CDATA[Depth]]></category>
		<category><![CDATA[dotNET/mono]]></category>
		<category><![CDATA[中文]]></category>
		<category><![CDATA[compiler]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[parser]]></category>
		<category><![CDATA[syntax]]></category>
		<category><![CDATA[~ninputer]]></category>

		<guid isPermaLink="false">http://typeof.net/?p=323</guid>
		<description><![CDATA[经过前面四篇的铺垫，我们终于拥有了编写语法分析器的强大工具，现在可以正式开发一门编程语言的语法分析器了。我们先来定义 miniSharp 的语法规则，然后根据 LL 文法的特点进行一些调整，最后借助解析器组合子生成完整的语法分析器。 miniSharp 语言是 C# 的一个小子集，然而它仍然具有一门完整编程语言的所有要素，而且仍然是一种面向对象的语言。我们把 miniSharp 的语法分成三类——声明结构、语句和表达式。声明结构就是类、方法、字段的声明。语句就是诸如 if-else、while 这样特定含义的指令。而表达式则是表示一种有运算结果的结构，如二元运算符表达式、函数调用表达式等。C# 中赋值也是一种表达式，但 miniSharp 为了简化后续代码生成，将赋值当成一种语句。 首先来定义声明结构的文法。为了简化语义分析，我们规定程序中第一个类必须是一个静态类，静态类中只能有一个静态方法Main——这是整个miniSharp唯一允许的静态方法。在静态类之后可以定义多个普通类，普通类之间可以继承。下面定义文法的产生式采用了扩展写法，支持类似克林闭包的 * 符号。G → X* 代表 G → ε; G → H; H → X G。 Program → MainClass ClassDecl* MainClass → static class ID { public static void Main (string[] ID) { Statement+ } } ClassDecl → class [...]]]></description>
			<content:encoded><![CDATA[<p>经过前面四篇的铺垫，我们终于拥有了编写语法分析器的强大工具，现在可以正式开发一门编程语言的语法分析器了。我们先来定义 miniSharp 的语法规则，然后根据 LL 文法的特点进行一些调整，最后借助解析器组合子生成完整的语法分析器。</p> 
 <span id="more-323"></span>
<p>miniSharp 语言是 C# 的一个小子集，然而它仍然具有一门完整编程语言的所有要素，而且仍然是一种面向对象的语言。我们把 miniSharp 的语法分成三类——声明结构、语句和表达式。声明结构就是类、方法、字段的声明。语句就是诸如 <code>if-else</code>、<code>while</code> 这样特定含义的指令。而表达式则是表示一种有运算结果的结构，如二元运算符表达式、函数调用表达式等。C# 中赋值也是一种表达式，但 miniSharp 为了简化后续代码生成，将赋值当成一种语句。</p> 
 
<p>首先来定义声明结构的文法。为了简化语义分析，我们规定程序中第一个类必须是一个静态类，静态类中只能有一个静态方法Main——这是整个miniSharp唯一允许的静态方法。在静态类之后可以定义多个普通类，普通类之间可以继承。下面定义文法的产生式采用了扩展写法，支持类似克林闭包的 <code>*</code> 符号。<i>G</i> → <i>X</i>* 代表 <i>G</i> → <i>ε</i>; <i>G</i> → <i>H</i>; <i>H</i> → <i>X</i> <i>G</i>。</p> 
 
<pre class="syntax">Program     →	MainClass ClassDecl*
MainClass   →	<b>static</b> <b>class</b> ID <b>{ public static void Main (string[]</b> ID<b>)</b> 
             	<b>{</b> Statement+ <b>} }</b> 
ClassDecl   →	<b>class</b> ID <b>{</b> FieldDecl<em> MethodDecl</em> <b>}</b> 
            →	<b>class</b> ID <b>:</b> ID <b>{</b> FieldDecl<em> MethodDecl</em> <b>}</b> 
FieldDecl   →	Type ID;
MethodDecl  →	<b>public</b> Type ID <b>(</b>FormalList<b>)</b> 
             	<b>{</b> Statement* <b>return</b> Exp <b>; }</b> 
FormalList  →	Type ID FormalRest*
            →	<i>ε</i> 
FormalRest  →	<b>,</b> Type ID
Type        →	<b>int[]</b> 
            →	<b>bool</b> 
            →	<b>int</b> 
            →	ID
 
</pre><p>语句部分我们将要定义语句块和六种语句。其中if-else语句的else部分是不能省略的。while语句不支持break。剩下四种分别是调用Console.WriteLine的语句、赋值语句、数组元素赋值语句和变量声明语句。</p> 
 
<pre class="syntax">Statement →	<b>{</b> Statement* <b>}</b> 
          →	<b>if (</b> Exp <b>)</b> Statement <b>else</b> Statement
          →	<b>while (</b> Exp <b>)</b> Statement
          →	<b>System.Console.WriteLine(</b> Exp <b>)</b> 
          →	ID <b>=</b> Exp <b>;</b> 
          →	ID <b>[</b> Exp <b>] =</b> Exp <b>;</b> 
          →	Type ID <b>;</b> 
 
</pre><p>表达式部分我们将要定义二元、一元、数组长度、数组访问、字面常量、变量、this引用、new运算、方法调用等多种表达式。</p> 
<pre class="syntax">Exp	→	Exp op Exp
	→	Exp[ Exp ]
	→	Exp .Length
	→	Exp .ID ( ExpList )
	→	INTEGER_LITERAL
	→	true
	→	false
	→	ID
	→	this
	→	new int [ Exp ]
	→	new ID ()
	→	! Exp
	→	( Exp )
ExpList	→	Exp ExpRest*
	→	ε
ExpRest	→	, Exp
 
</pre><p>其中二元运算表达式的 op 是 <code>+</code>、<code>-</code>、<code>*</code>、<code>/</code>、<code>&gt;</code>、<code>&lt;</code>、<code>==</code>、<code>&amp;&amp;</code> 和 <code>||</code> 之一。为了简单起见我们这里的二元运算表达式文法是有歧义而且没有正确定义优先级的。按照 C# 的语言规范，运算符的优先级关系如下（只提取了 miniSharp 支持的部分）：</p> 
 
<table> <tbody> <tr><th>优先级</th><th>运算符</th></tr><tr> <td>1</td> <td>(Exp)&nbsp; new this 变量 常量<br />方法调用 属性访问 数组访问</td></tr> <tr> <td>2</td> <td>!</td></tr> <tr> <td>3</td> <td>* /</td></tr> <tr> <td>4</td> <td>+ -</td></tr> <tr> <td>5</td> <td>&lt; &gt; ==</td></tr> <tr> <td>6</td> <td>&amp;&amp;</td></tr> <tr> <td>7</td> <td>||</td></tr></tbody></table> 
<p>有些语法分析器就是使用有歧义的二元运算符文法，在遇到歧义时使用预定义的运算符优先级来解决冲突。现在的语法分析器倾向于直接使用无歧义的文法。下面的文法就是经过精心安排的运算符文法，消除了歧义并使得运算符具有左结合和优先级的区别：</p> 
 
<pre class="syntax">BasicExp    →	括号、new、this、变量、常量、方法调用、属性访问、数组访问
Factor      →	BasicExp
            →	! Factor
Term        →	Factor
            →	Term op Factor   其中 op 是 * /
Comparand   →	Term
            →	Comparand op Term   其中 op 是 + -
Comparison  →	Comparand
            →	Comparison op Comparand    其中 op 是 < > ==
Logical	    →	Comparison
            →	Logical &#038;&#038; Comparison
Exp         →	Logical
            →	Exp || Logical
 
</pre><p>这样我们就定义了miniSharp的完整文法。注意，上述文法仍然存在一些左公因式和左递归的现象。我们用的解析器组合子因为独特的广度优先分支判断方式，其支持的文法实际上已经超越了递归下降语法分析器的 <span class="math">LL(<i>k</i>)</span> 文法，称作 <span class="math">LL(∞)</span> 的文法，这种文法非常强大，可描述所有确定性下推自动机 DPDA 接受的语言。但是，它仍然不允许文法存在左递归，而左公因式也会大大降低解析器的效率。所以消除左递归和尽量避免左公因式仍然是真正实现语法分析器时需要着重考虑的任务。</p> 
 
<p>现代语言的语法分析器通常都是将源代码翻译成一棵抽象语法树（Abstract Syntax Tree，AST）。后续的语义分析可以在抽象语法树上继续进行。我们在语法分析篇（六）介绍过“语法分析树”，它是一种在文法推导过程中建立起来的树状数据结构。那么什么是抽象语法树呢？其实就是经过简化和抽象的语法分析树。在完整的语法分析树中每个推导过程的终结符都包含在语法树内，而且每个非终结符都是不同的节点类型。实际上，如果仅仅是要做编译器的话，很多终结符（如关键字、各种标点符号）是无需出现在语法树里的；而前面表达式文法中的 Factor、Term 也实际上没有必要区分为两种不同的类型，可以将其抽象为 BinaryExpression 类型。这样简化、抽象之后的语法树，更加利于后续语义分析和代码生成。使用 0.NET 里的面向对象语言来实现语法树，最常见的做法就是用组合模式，将语法树做成一颗对象树，每种抽象语法对应一个节点类。下图就是 miniSharp 的抽象语法树的所有类。</p> 
 
<div class="imagebox"><img src="http://images.cnblogs.com/cnblogs_com/Ninputer/201107/201107080119026388.png"></img></div><p>节选其中一个语法树节点展示一下，比如大家熟悉的IfElse语句的语法树节点类：</p> 
 
<pre class="code cs">public class IfElse : Statement
{
	public Expression Condition { get; private set; }
	public Statement TruePart { get; private set; }
	public Statement FalsePart { get; private set; }
	public SourceSpan IfSpan { get; private set; }
	public SourceSpan ElseSpan { get; private set; }
 
	public IfElse(Expression cond, Statement truePart, Statement falsePart, SourceSpan ifSpan, SourceSpan elseSpan)
	{
		Condition = cond;
		TruePart = truePart;
		FalsePart = falsePart;
		IfSpan = ifSpan;
		ElseSpan = elseSpan;
	}
 
	public override T Accept<T>(IAstVisitor<T> visitor)
	{
		return visitor.VisitIfElse(this);
	}
}
 
</pre><p>它的结构非常简单，里面保存了所有作为子节点成分的字段，例如 IfElse 是由一个 Condition 表达式和 TruePart、FalsePart 两个语句组成。另外我们还多储存了两个 SourceSpan，分别是 if 语句中“<code>if</code>”关键字和“<code>else</code>”关键字出现的源代码位置（多少行，多少列）。保存位置是为了后续语义分析中提供错误信息的位置。比如 if 的条件表达式必须是个 bool 类型的表达式，但语法分析阶段无法做出类型验证，而到了语义分析阶段分析出了语义错误，仍然需要向编译器用户提供错误的位置，这些 SourceSpan 就可以派上用场。</p> 
 
<p>注意节点类最后还实现了一个 Accept 方法，用来支持语法树的 Visitor 模式。我们在语义分析阶段和代码生成阶段，需要一次又一次地遍历抽象语法树。为了简化语法树的访问，我们声明一个 <code>IAstVisitor&lt;T&gt;</code> 接口作为语法树的 Visitor，后续过程需要遍历语法树时，就实现这一接口即可。实际上这个接口有一个默认实现—— AstVisitor 类，允许只重写一部分成员。</p> 
 
<p>有了 Ast，下面我们就开始编写 miniSharp 的语法分析器。在本系列的第五篇中我们已经用 VBF 词法分析库定义了 miniSharp 的词法，生成了一些 Token 对象。那么接下来就只要使用 Linq 语法的解析器组合子，根据本篇开头定义的文法进行组合，并适时使用 select 语句生成语法树节点的对象即可。比如，文法最开始的 Program 和 MainClass 的写法如下：</p> 
 
<pre class="code cs">PProgram.Reference = // MainClass ClassDecl*
	from main in PMainClass
	from classes in PClassDecl.Many()
	select new Program(main, classes);
 
PMainClass.Reference = // static class id { public static void Main(string[] id) { statement }}
	from _static1 in K_STATIC
	from _class in K_CLASS
	from className in ID
	from _1 in LEFT_BR
	from _public in K_PUBLIC
	from _static2 in K_STATIC
	from _void in K_VOID
	from _main in K_MAIN
	from _2 in LEFT_PH
	from _string in K_STRING
	from _3 in LEFT_BK
	from _4 in RIGHT_BK
	from arg in ID
	from _5 in RIGHT_PH
	from _6 in LEFT_BR
	from statements in PStatement.Many1()
	from _7 in RIGHT_BR
	from _8 in RIGHT_BR
	select new MainClass(className, arg, statements);
 
</pre><p>这代码是如此的直白以至于没什么可解释的。唯一要注意的是 <code>PProgram.Reference</code> 这个用法，这里 PProgram 是 <code>ParserReference&lt;T&gt;</code> 类的实例。这个类允许先直接 new 出来，然后再用 <code>.Reference = XXX</code> 的方式为其指定语法规则。这样就允许一个 Parser 组合子先使用，后定义（比如上面例子中的 PMainClass 就先在 PProgram 的语法定义中使用了，然后下面才定义其语法）。因为文法中的非终结符常常出现递归引用，用 ParserReference 这个类可以大大简化我们的工作，不用关心 Parser 的声明先后顺序问题。</p> 
 
<p>我们重点来看一些需要特殊技巧的例子。首先是声明方法形式参数的文法，采用了 FormalList → Type ID FormalRest* 这样的定义方法，这是避免左递归的技巧。但是这样一来，方法的第一个参数就和其他的参数分别定义在两个语法当中。我们希望生成的抽象语法树不区分第一个参数和其余参数，所以可以在生成语法树时采用一点点小技巧来办到：</p> 
 
<pre class="code cs">var paramFormal =
	from paramType in PType
	from paramName in ID
	select new Formal(paramType, paramName);
 
PFormalList.Reference = // Type id FormalRest* | <empty> 
	(from first in paramFormal
	 from rest in PFormalRest.Many()
	 select new[] { first }.Concat(rest).ToArray()) |
	Parsers.Succeed(new Formal[0]);
 
PFormalRest.Reference = // , Type id
	paramFormal.PrefixedBy(COMMA.AsParser());
 
</pre><p>另外注意扩展的产生式 “X*” 在VBF解析器组合子库中可以直接使用 <code>X.Many()</code> 的方式实现。VBF 中还定义了数个这种方便的扩展组合子。</p> 
 
<p>最后要注意的是二元运算符的分析器。我们前面写出的无歧义符合优先级的二元运算符文法仍然是左递归的，用于解析器组合子时必须像上面的 FormalList 那样改成右递归的。但是这些运算符都是左结合的，我们不想让生成的抽象语法树也变成右递归的形态。因此，这里我们需要用（传统） Linq 的 Aggregate 扩展方法来处理一下生成的语法树：</p> 
 
<pre class="code cs">var termRest =
	from op in (ASTERISK.AsParser() | SLASH.AsParser())
	from factor in PFactor
	select new { Op = op, Right = factor };
 
PTerm.Reference = // term * factor | factor
	from factor in PFactor
	from rest in termRest.Many()
	select rest.Aggregate(factor, (f, r) => new Binary(r.Op, f, r.Right));
 
var comparandRest =
	from op in (PLUS.AsParser() | MINUS.AsParser())
	from term in PTerm
	select new { Op = op, Right = term };
 
PComparand.Reference = // comparand + term | term
	from term in PTerm
	from rest in comparandRest.Many()
	select rest.Aggregate(term, (t, r) => new Binary(r.Op, t, r.Right));
 
 
var comparisonRest =
	from op in (LESS.AsParser() | GREATER.AsParser() | EQUAL.AsParser())
	from comparand in PComparand
	select new { Op = op, Right = comparand };
 
PComparison.Reference = // comparison < comparand | comparand
	from comparand in PComparand
	from rest in comparisonRest.Many()
	select rest.Aggregate(comparand, (c, r) => new Binary(r.Op, c, r.Right));
 
</pre><p>  除此之外，剩下的语法翻译成组合子基本上都是水到渠成的工作了。完整的代码全部都在 <code>MiniSharpParser.cs</code> 中，大家可以自行下载阅读。经过小小的努力，我们终于能将 miniSharp 的源代码转换为抽象语法树了，接下来我们就要进入下一个编译器重要的阶段——语义分析。敬请期待下一篇！</p> 
 ]]></content:encoded>
			<wfw:commentRss>http://typeof.net/2011/07/compiler-by-myself-10/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>自己动手开发编译器（九）CPS风格的解析器组合子</title>
		<link>http://typeof.net/2011/07/compiler-by-myself-9/</link>
		<comments>http://typeof.net/2011/07/compiler-by-myself-9/#comments</comments>
		<pubDate>Sat, 16 Jul 2011 11:56:48 +0000</pubDate>
		<dc:creator>Belleve Invis</dc:creator>
				<category><![CDATA[Depth]]></category>
		<category><![CDATA[dotNET/mono]]></category>
		<category><![CDATA[中文]]></category>
		<category><![CDATA[compiler]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[parser]]></category>
		<category><![CDATA[syntax]]></category>
		<category><![CDATA[~ninputer]]></category>

		<guid isPermaLink="false">http://typeof.net/?p=303</guid>
		<description><![CDATA[上回我们用函数式编程的方法，结合 Linq 语法，建立了一套解析器组合子方案，并能成功解析自定义文法的输入字符串。但是，上次做成的解析器组合子有个重要的功能没有完成——错误报告。作为编程语言的语法分析器，不能在遇到语法错误的时候简单地返回 null ，那样程序员就很难修复代码中的语法错误。我们需要的是准确报告语法错误的位置，更进一步，是程序中所有的语法错误，而不仅仅是头一个。后者要求解析器具有错误恢复的能力，即在遇到语法错误之后，还能恢复到正常状态继续解析。错误恢复不仅仅可以用在检测出所有的语法错误，还可以在存在语法错误的时候仍然提供有意义的解析结果，从而用于 IDE 的智能感知和重构等功能。手写的递归下降语法分析器可以很容易地加入错误恢复，但需要针对每一处错误手工编写代码来恢复。像 C#官方编译器，给出的语法错误信息非常全面、精确、智能，全都是手工编写的功劳。又回到我们是懒人这个残酷的事实，能不能在让解析器组合子生成的解析器自动具有错误恢复能力呢？ 首先来看上一个版本的四个基本组合子：空产生式的 Succeed 组合子， token 产生式的 AsParser 组合子，连接运算产生式的 SelectMany 组合子和并运算产生式的 Union 组合子。首先 Succeed 是不会解析失败的，所以它没有必要进行错误恢复。现在来看 AsParser 组合子，它的逻辑是读取下一个词素，如果词素的单词类型和组合子的参数匹配则解析成功，否则解析失败。代码如下： public static ParserFunc&#60;Lexeme&#62; AsParser(this Token token) { return scanner =&#62; { var lexeme = scanner.Read(); if (lexeme.TokenIndex == token.Index) { return new Result&#60;Lexeme&#62;(lexeme, scanner); } else { return null; } }; } [...]]]></description>
			<content:encoded><![CDATA[<p>上回我们用函数式编程的方法，结合 Linq 语法，建立了一套解析器组合子方案，并能成功解析自定义文法的输入字符串。但是，上次做成的解析器组合子有个重要的功能没有完成——错误报告。作为编程语言的语法分析器，不能在遇到语法错误的时候简单地返回 null ，那样程序员就很难修复代码中的语法错误。我们需要的是准确报告语法错误的位置，更进一步，是程序中<strong>所有的语法错误</strong>，而不仅仅是头一个。后者要求解析器具有<strong>错误恢复</strong>的能力，即在遇到语法错误之后，还能恢复到正常状态继续解析。错误恢复不仅仅可以用在检测出所有的语法错误，还可以在存在语法错误的时候仍然提供有意义的解析结果，从而用于 IDE 的智能感知和重构等功能。手写的递归下降语法分析器可以很容易地加入错误恢复，但需要针对每一处错误手工编写代码来恢复。像 C#官方编译器，给出的语法错误信息非常全面、精确、智能，全都是手工编写的功劳。又回到我们是懒人这个残酷的事实，能不能在让解析器组合子生成的解析器自动具有错误恢复能力呢？</p>
<span id="more-303"></span>
<p>首先来看上一个版本的四个基本组合子：空产生式的 Succeed 组合子， token 产生式的 AsParser 组合子，连接运算产生式的 SelectMany 组合子和并运算产生式的 Union 组合子。首先 Succeed 是不会解析失败的，所以它没有必要进行错误恢复。现在来看 AsParser 组合子，它的逻辑是读取下一个词素，如果词素的单词类型和组合子的参数匹配则解析成功，否则解析失败。代码如下：</p> <pre class="code mghl cs"><span class="keyword">public static </span><span class="identifier type">ParserFunc</span>&lt;<span class="identifier type">Lexeme</span>&gt; AsParser(<span class="keyword">this </span><span class="identifier type">Token </span>token)
{
    <span class="keyword">return </span>scanner =&gt;
    {
        <span class="keyword">var </span>lexeme = scanner.Read();
        <span class="keyword">if </span>(lexeme.TokenIndex == token.Index)
        {
            <span class="keyword">return new </span><span class="identifier type">Result</span>&lt;<span class="identifier type">Lexeme</span>&gt;(lexeme, scanner);
        }
        <span class="keyword">else
        </span>{
            <span class="keyword">return null</span>;
        }
    };
}
</pre>
<p>如果要对失败的情形进行错误恢复，有两种可行的选择： 1 、假装要解析的 Token 存在，继续解析（这种做法相当于在原位置<strong>插入</strong>了一个单词）； 2 、跳过不匹配的单词，重新进行解析（这种做法相当于<strong>删除</strong>了一个单词）。如果漏写一个分号或者括号，插入型错误恢复就能有效地恢复错误，如果是多写了一个关键字或标识符造成的错误，删除型错误恢复就能有效地恢复。但问题是，我们怎么能在组合子的代码中判断出哪种错误恢复更有效呢？最优策略是让两种错误恢复的状态都继续解析到末尾，然后看哪种恢复状态整体语法错误最少。但是，只要有一个字符解析失败，就要分支成两个完整解析，那么错误一旦多起来，这个分支的庞大程度将使得错误恢复无法进行。更何况，错误并不仅仅出现在真正的语法错误上，我们还要用错误来判断“并”运算组合子的分支问题。请看上一版本 Union 组合子的代码：</p>
<pre class="code mghl cs"><span class="keyword">public static </span><span class="identifier type">ParserFunc</span>&lt;T&gt; Union&lt;T&gt;(<span class="keyword">this </span><span class="identifier type">ParserFunc</span>&lt;T&gt; parser1, <span class="identifier type">ParserFunc</span>&lt;T&gt; parser2)
{
    <span class="keyword">return </span>scanner =&gt;
    {
        <span class="keyword">var </span>scanner1 = scanner;
        <span class="keyword">var </span>scanner2 = scanner.Fork();

        <span class="keyword">var </span>result1 = parser1(scanner1);
        <span class="keyword">if </span>(result1 != <span class="keyword">null</span>)
        {

            <span class="keyword">return </span>result1;
        }

        <span class="keyword">var </span>result2 = parser2(scanner2);

        <span class="keyword">return </span>result2;
    };
}
</pre>
<p>在 Union 中，我们先试验第一个 parser1 能否解析成功，如果失败才解析 parser2 。如果解析器有自动错误恢复的功能，那么我们就无法用这种方式判断了，因为两条分支遇到错误之后都会继续进行下去。我们可以让两条分支都解析到底，然后挑错误较少的分支作为正式解析结果。但同上所述，这种做法的分支多得难以置信，效率上决定我们不能采用。</p>

<p>为了避免效率问题，我们需要一种“广度优先”的处理方案。在遇到错误时产生的“插入”和“删除”两条分支，要同时进行，但要一步一步地进行。这里所谓的一“步”，就是指 AsParser 组合子读取一个词素。我们看到四种基本组合子中，只有 AsParser 组合子会用 scanner 来真正读取词素，其他组合子最终也是要调用到 AsParser 组合子来进行解析的。我们让两个可能的分支都向前解析一步，然后看是否其中一条分支的结果比另外一条更好。所谓更好，就是一条分支没有进一步遇到错误，而另外一条分支遇到了错误。如果两条分支都没有遇到错误，或者都遇到了错误，我们就再向前推进一步，直到某一步比另外一步更好为止。 Union 组合子也可以采用同样的策略处理。这是一种贪心算法的策略，我们所得到的结果未必是语法错误最少的解析结果，但它的效率是可以接受的。</p>

<p>那么怎么进行“广度优先”推进呢？我们上次引入的组合子，当前的组合子无法知道下一个要运行的组合子是什么，更无法控制下一个组合子只向前解析一步。为了达到目的，我们要引入一种新的组合子函数原型，称作<strong>CPS</strong>（ Continuation Pass-in Style ）风格的组合子。不知道大家有多少人听说过 CPS ，这在函数式编程界是一种广为应用的模式，在.NET 世界里其实也有采用。.NET 4.0 引入的 Task Parallel Library 库中的 Task 类，就是一个典型的 CPS 设计范例。我举一个简单的例子来介绍一下 CPS 。如果有两个函数 A 和 B ，需要按顺序调用，用传统方式编程很简单，就是直接调用：</p>
<pre class="code mghl cs"><span class="keyword">void </span>A()
{
    <span class="identifier type">Console</span>.WriteLine(<span class="string">"A"</span>);
}

<span class="keyword">void </span>B()
{
    <span class="identifier type">Console</span>.WriteLine(<span class="string">"B"</span>);
}

<span class="keyword">void </span>Run()
{
    A();
    B();
}</pre>
<p>而如果采用 CPS ，则是把 B 传递给 A ，这时我们称 B 是 A 的 continuation ，或者 future 。</p>
<pre class="code mghl cs"><span class="keyword">void </span>A(<span class="identifier type">Action </span>future)
{
    <span class="identifier type">Console</span>.WriteLine(<span class="string">"A"</span>);
    future();
}

<span class="keyword">void </span>B()
{
    <span class="identifier type">Console</span>.WriteLine(<span class="string">"B"</span>);
}

<span class="keyword">void </span>Run()
{
    A(B);
}
</pre>
<p>乍一看这也不能实现什么特别的事情，但其实是很有用的。 A 获得了自己的 future 之后可以自行决定如何运行它。比如可以异步地运行。这样我们就在 Run()方法中，用一种容易理解的方式，构建出了一条异步调用序列。.NET 4.0 的 Task Parallel Library 正是这样的风格，每个 Task 类通过 ContinueWith 方法接受自己的 future 。而我们的函数式解析其组合子，也可以用这种方式，让每个 Parser 函数接受一个 future ，并自行决定如何调用 future 。这里最关键的思想是实现<strong>延迟调用</strong>future ，从而实现“广度优先”的单步解析效果。首先来看看新的 Parser 类原型（警告，这一篇里采用的函数式技巧比上一篇还要难懂得多，如果看了之后发生头晕，嗜睡等症状，请休息之后重新看……）：</p>
<p>
<pre class="code mghl cs"><span class="keyword">public delegate </span><span class="identifier type">Result</span>&lt;T&gt; <span class="identifier type">ParserFunc</span>&lt;T&gt;(<span class="identifier type">ForkableScanner </span>scanner, <span class="identifier type">ParserContext </span>context);
<span class="keyword">public delegate </span><span class="identifier type">ParserFunc</span>&lt;TFuture&gt; <span class="identifier type">Future</span>&lt;T, TFuture&gt;(T value);

<span class="keyword">public abstract class </span><span class="identifier type">Parser</span>&lt;T&gt;
{
    <span class="keyword">public abstract </span><span class="identifier type">ParserFunc</span>&lt;TFuture&gt; BuildParser&lt;TFuture&gt;(<span class="identifier type">Future</span>&lt;T, TFuture&gt; future);
}
</pre></p>
<p>ParserFunc 方法和上一篇非常类似，但是多了一个 ParserContext 方法。我们会用这个对象来保存一些错误报告的信息。再来是 Future 函数的定义， Future 返回的类型是一个 ParserFunc 委托对象； Future 的参数 T ，则是用来让每一个组合子生成的 Parser ，将自己的解析结果 T 传给它自己的 Future 用的。注意这次多了一个 Parser&lt;T&gt;抽象类，它代替 ParserFunc 成为组合子的生成对象。它之所以不能声明成一个委托，是因为它的 BuildParser 方法要接受一个额外的泛型类型参数 TFuture 。接下来每一个解析器组合子都需要继承自 Parser&lt;T&gt;，并实现它的 BuildParser 方法。下面我们就来看一看新的 CPS 型解析器组合子怎么定义。</p>

<p>首先还是 G → ε的组合子，它永远都能解析成功，所以，它的逻辑是生成一个 ParserFunc ，将预设的解析结果传递给自己的 Future:</p>
<pre class="code mghl cs"><span class="keyword">public class </span><span class="identifier type">SucceedParser</span>&lt;T&gt; : <span class="identifier type">Parser</span>&lt;T&gt;
{
    <span class="keyword">public </span>T Value { <span class="keyword">get</span>; <span class="keyword">private set</span>; }

    <span class="keyword">public </span>SucceedParser(T value)
    {
        Value = value;
    }
    <span class="keyword">public override </span><span class="identifier type">ParserFunc</span>&lt;TFuture&gt; BuildParser&lt;TFuture&gt;(<span class="identifier type">Future</span>&lt;T, TFuture&gt; future)
    {
        <span class="keyword">return </span>(scanner, context) =&gt; future(Value)(scanner, context);
    }
}
</pre>
<p>这是第一次实践 CPS 风格，大家一定要注意观察它与上一次传统风格解析器组合子的不同。最关键的一点，就是返回的 ParserFunc ，必须要调用 BuildParser 传进来的 future 函数，传递自己的解析结果。</p>

<p>接下来就是重头戏 G → t ，我们要在这个单词解析器组合子中加入期待已久的错误报告和错误恢复功能，请看代码：</p>
<pre class="code mghl cs"><span class="keyword">public class </span><span class="identifier type">TokenParser </span>: <span class="identifier type">Parser</span>&lt;<span class="identifier type">Lexeme</span>&gt;
{
    <span class="keyword">public </span><span class="identifier type">Token </span>ExpectedToken { <span class="keyword">get</span>; <span class="keyword">private set</span>; }
    <span class="keyword">public string </span>MissingCorrection { <span class="keyword">get</span>; <span class="keyword">private set</span>; }

    <span class="keyword">public </span>TokenParser(<span class="identifier type">Token </span>expected)
    {
        ExpectedToken = expected;
        MissingCorrection = expected.ToString();
    }


    <span class="keyword">public override </span><span class="identifier type">ParserFunc</span>&lt;TFuture&gt; BuildParser&lt;TFuture&gt;(<span class="identifier type">Future</span>&lt;<span class="identifier type">Lexeme</span>, TFuture&gt; future)
    {
        <span class="identifier type">ParserFunc</span>&lt;TFuture&gt; scan = <span class="keyword">null</span>;
        scan = (scanner, context) =&gt;
        {
            <span class="keyword">var </span>s1 = scanner.Fork();

            <span class="keyword">var </span>l = scanner.Read();

            <span class="keyword">int </span>tokenIndex;
            tokenIndex = l.TokenIndex;

            <span class="keyword">if </span>(tokenIndex == ExpectedToken.Index)
            {
                <span class="keyword">var </span>r = context.StepResult(0, () =&gt; future(l)(scanner, context));
                <span class="keyword">return </span>r;
            }
            <span class="keyword">else
            </span>{
                <span class="identifier type">Lexeme </span>correctionLexeme = l.GetErrorCorrectionLexeme(ExpectedToken.Index, MissingCorrection);
                <span class="identifier type">ErrorCorrection </span>insertCorrection = <span class="keyword">new </span><span class="identifier type">InsertedErrorCorrection</span>(ExpectedToken.ToString(), correctionLexeme.Span);

                <span class="keyword">if </span>(l.IsEndOfStream)
                {
                    scanner.Join(s1);
                    <span class="keyword">return </span>context.StepResult(1, () =&gt; future(correctionLexeme)(scanner, context), insertCorrection); <span class="comment">//插入
                </span>}
                <span class="keyword">else
                </span>{
                    <span class="identifier type">ErrorCorrection </span>deleteCorrection = <span class="keyword">new </span><span class="identifier type">DeletedErrorCorrection</span>(l);
                    <span class="keyword">return </span>context.ChooseBest(context.StepResult(1, () =&gt; future(correctionLexeme)(s1, context), insertCorrection), <span class="comment">//插入
                        </span>context.StepResult(1, () =&gt; scan(scanner, context), deleteCorrection)); <span class="comment">//删除
                </span>}
            }
        };

        <span class="keyword">return </span>scan;
    }
}</pre>
<p>大致描述下来就是生成这样一个 ParserFunc ：首先通过 Scanner 读取下一个词素，并判断它是否是期待的单词。如果是，则调用 context.StepResult(0, …)方法（稍后解释）；如果不是，则判断是否遇到的输入流的末尾，如果是末尾，则只尝试“插入”修复方案（因为无法删除“流末尾”单词），如果不是末尾则使用 context.ChooseBest 方法，尝试插入和删除两种修复方案。 context.StepResult 方法就是产生延迟执行 future 的关键。它的第一个参数表示该结果的“代价”， 0 表示这是一个成功解析的结果； 1 表示是经过错误恢复的结果。第二个参数则是一个延迟执行的委托，这个委托只会在我们需要将解析器“推进一步”的时候才会执行，我们将 future 函数的调用放在这里并做成延迟执行的方式，就是要等待广度优先一步一步地向前解析时才执行下一步的操作。那么这个 context.ChooseBest 函数到底是如何实现的呢？请看代码：</p>
<pre class="code mghl cs"><span class="keyword">private </span><span class="identifier type">Result</span>&lt;T&gt; ChooseBest&lt;T&gt;(<span class="identifier type">Result</span>&lt;T&gt; result1, <span class="identifier type">Result</span>&lt;T&gt; result2, <span class="keyword">int </span>correctionDepth)
{
    <span class="keyword">if </span>(result1.Type == <span class="identifier type">ResultType</span>.Stop)
    {
        <span class="keyword">return </span>result1;
    }
    <span class="keyword">if </span>(result2.Type == <span class="identifier type">ResultType</span>.Stop)
    {
        <span class="keyword">return </span>result2;
    }

    <span class="keyword">var </span>step1 = (<span class="identifier type">StepResult</span>&lt;T&gt;)result1;
    <span class="keyword">var </span>step2 = (<span class="identifier type">StepResult</span>&lt;T&gt;)result2;

    <span class="keyword">if </span>(step1.Cost &lt; step2.Cost)
    {
        <span class="keyword">return </span>step1;
    }
    <span class="keyword">else if </span>(step1.Cost &gt; step2.Cost)
    {
        <span class="keyword">return </span>step2;
    }
    <span class="keyword">else
    </span>{<span class="comment">
        </span><span class="keyword">return new </span><span class="identifier type">StepResult</span>&lt;T&gt;(<span class="identifier type">Math</span>.Max(step1.Cost, step2.Cost), 
            () =&gt; ChooseBest(step1.NextResult, step2.NextResult, correctionDepth + 1), <span class="keyword">null</span>);
    }
}</pre>
<p>ChooseBest 方法要比较两个 Result 的代价，并选取代价较小的分支。如果代价一样，则通过延迟计算的方法将比较推至下一轮。我们到处采用延迟计算的手段，以至于整个单词流都输入之后，解析可能仍然没有结束！所以 Result 类有一个集中取得每一步结果的功能，在单词流输入完毕后还要继续驱动这些延迟计算，直到拿到最终的解析结果。</p>

<p>接下来是表示连接运算 G → X Y 的 SelectMany 组合子。具体方法是将传入的 future 作为 Y 的 future ，再将 Y 的 Parser 作为 X 的 future ，以此将两者连接起来：</p>
<pre class="code mghl cs"><span class="keyword">public class </span><span class="identifier type">ConcatenationParser</span>&lt;T1, T2, TR&gt; : <span class="identifier type">Parser</span>&lt;TR&gt;
{
    <span class="keyword">public </span><span class="identifier type">Parser</span>&lt;T1&gt; ParserLeft { <span class="keyword">get</span>; <span class="keyword">private set</span>; }
    <span class="keyword">public </span><span class="identifier type">Func</span>&lt;T1, <span class="identifier type">Parser</span>&lt;T2&gt;&gt; ParserRightSelector { <span class="keyword">get</span>; <span class="keyword">private set</span>; }
    <span class="keyword">public </span><span class="identifier type">Func</span>&lt;T1, T2, TR&gt; Selector { <span class="keyword">get</span>; <span class="keyword">private set</span>; }

    <span class="keyword">public </span>ConcatenationParser(<span class="identifier type">Parser</span>&lt;T1&gt; parserLeft, <span class="identifier type">Func</span>&lt;T1, <span class="identifier type">Parser</span>&lt;T2&gt;&gt; parserRightSelector, <span class="identifier type">Func</span>&lt;T1, T2, TR&gt; selector)
    {
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(parserLeft, <span class="string">"parserLeft"</span>);
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(parserRightSelector, <span class="string">"parserRightSelector"</span>);
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(selector, <span class="string">"selector"</span>);

        ParserLeft = parserLeft;
        ParserRightSelector = parserRightSelector;
        Selector = selector;
    }

    <span class="keyword">public override </span><span class="identifier type">ParserFunc</span>&lt;TFuture&gt; BuildParser&lt;TFuture&gt;(<span class="identifier type">Future</span>&lt;TR, TFuture&gt; future)
    {
        <span class="keyword">return </span>(scanner, context) =&gt; 
            ParserLeft.BuildParser(
                left =&gt; ParserRightSelector(left).BuildParser(
                    right =&gt; future(Selector(left, right))))(scanner, context);
    }
}
</pre>

<p>最后的并运算，则是广度优先同时实验两个传入的 Parser ，即直接用 ChooseBest 方法选取继续执行的 Parser ：</p>
<pre class="code mghl cs"><span class="keyword">public class </span><span class="identifier type">AlternationParser</span>&lt;T&gt; : <span class="identifier type">Parser</span>&lt;T&gt;
{
    <span class="keyword">public </span><span class="identifier type">Parser</span>&lt;T&gt; Parser1 { <span class="keyword">get</span>; <span class="keyword">private set</span>; }
    <span class="keyword">public </span><span class="identifier type">Parser</span>&lt;T&gt; Parser2 { <span class="keyword">get</span>; <span class="keyword">private set</span>; }

    <span class="keyword">public </span>AlternationParser(<span class="identifier type">Parser</span>&lt;T&gt; parser1, <span class="identifier type">Parser</span>&lt;T&gt; parser2)
    {
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(parser1, <span class="string">"parser1"</span>);
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(parser2, <span class="string">"parser2"</span>);

        Parser1 = parser1;
        Parser2 = parser2;
    }

    <span class="keyword">public override </span><span class="identifier type">ParserFunc</span>&lt;TFuture&gt; BuildParser&lt;TFuture&gt;(<span class="identifier type">Future</span>&lt;T, TFuture&gt; future)
    {
        <span class="keyword">return </span>(scanner, context) =&gt;
        {
            <span class="keyword">var </span>s1 = scanner;
            <span class="keyword">var </span>s2 = scanner.Fork();

            <span class="keyword">return </span>context.ChooseBest(Parser1.BuildParser(future)(s1, context), Parser2.BuildParser(future)(s2, context));
        };
    }
}
</pre>

<p>如果大家还不能很清晰地理解上述 CPS 风格解析器组合子的原理，也不要担心。我也是花了整整两个星期时间反复看论文才理清所有细节的。而且我贴的也是简化的代码，并不完整。大家可以下载 VBF 库的源代码来仔细研究。当然，如果对 Haskell 不恐惧的话，看原始的论文也不错。从这里下载论文（点右上方 Download 下面的 PDF 图标）：<a title="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.30.7601" href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.30.7601">http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.30.7601</a></p>

<p>最后，我们像上一篇传统解析器组合子那样，为每种组合子声明一个便于使用的静态函数或者扩展方法。注意，在上述四种基本组合子外， CPS 组合子如要正常工作，需要一个特殊的组合子—— EndOfStreamParser ，它类似于 TokenParser 但错误恢复的时候从不尝试插入。这里略过它的实现，直接来看辅助函数的定义：</p>
<pre class="code mghl cs"><span class="keyword">public static class </span><span class="identifier type">Parsers
</span>{
    <span class="keyword">public static </span><span class="identifier type">Parser</span>&lt;<span class="identifier type">Lexeme</span>&gt; AsParser(<span class="keyword">this </span><span class="identifier type">Token </span>token)
    {
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(token, <span class="string">"token"</span>);

        <span class="keyword">return new </span><span class="identifier type">TokenParser</span>(token);
    }

    <span class="keyword">public static </span><span class="identifier type">Parser</span>&lt;<span class="identifier type">Lexeme</span>&gt; Eos()
    {
        <span class="keyword">return new </span><span class="identifier type">EndOfStreamParser</span>();
    }

    <span class="keyword">public static </span><span class="identifier type">Parser</span>&lt;T&gt; Succeed&lt;T&gt;(T value)
    {
        <span class="keyword">return new </span><span class="identifier type">SucceedParser</span>&lt;T&gt;(value);
    }

    <span class="keyword">public static </span><span class="identifier type">Parser</span>&lt;TResult&gt; SelectMany&lt;T1, T2, TResult&gt;(<span class="keyword">this </span><span class="identifier type">Parser</span>&lt;T1&gt; parser, <span class="identifier type">Func</span>&lt;T1, <span class="identifier type">Parser</span>&lt;T2&gt;&gt; parserSelector, <br />        <span class="identifier type">Func</span>&lt;T1, T2, TResult&gt; resultSelector ）
    {
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(parser, <span class="string">"parser"</span>);
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(parserSelector, <span class="string">"parserSelector"</span>);
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(resultSelector, <span class="string">"resultSelector"</span>);

        <span class="keyword">return new </span><span class="identifier type">ConcatenationParser</span>&lt;T1, T2, TResult&gt;(parser, parserSelector, resultSelector);
    }

    <span class="keyword">public static </span><span class="identifier type">Parser</span>&lt;T&gt; Union&lt;T&gt;(<span class="keyword">this </span><span class="identifier type">Parser</span>&lt;T&gt; parser1, <span class="identifier type">Parser</span>&lt;T&gt; parser2)
    {
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(parser1, <span class="string">"parser1"</span>);
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(parser2, <span class="string">"parser2"</span>);

        <span class="keyword">return new </span><span class="identifier type">AlternationParser</span>&lt;T&gt;(parser1, parser2);
    }
}</pre>

<p>这样，我们就能和上一篇一样用 Linq 的语法来组合 Parser 了。最后我们还需要一个驱动延迟计算的类：</p>
<pre class="code mghl cs"><span class="keyword">public class </span><span class="identifier type">ParserRunner</span>&lt;T&gt;
{
    <span class="keyword">public </span><span class="identifier type">ParserContext </span>Context { <span class="keyword">get</span>; <span class="keyword">private set</span>; }
    <span class="keyword">private </span><span class="identifier type">ParserFunc</span>&lt;T&gt; m_runner;

    <span class="keyword">public </span>ParserRunner(<span class="identifier type">Parser</span>&lt;T&gt; parser, <span class="identifier type">ParserContext </span>context)
    {
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(parser, <span class="string">"parser"</span>);
        <span class="identifier type">CodeContract</span>.RequiresArgumentNotNull(context, <span class="string">"context"</span>);

        m_runner = parser.BuildParser(FinalFuture);
        <span class="identifier type">Debug</span>.Assert(m_runner != <span class="keyword">null</span>);
        Context = context;
    }

    <span class="keyword">public </span>T Run(<span class="identifier type">ForkableScanner </span>scanner)
    {
        <span class="keyword">var </span>result = m_runner(scanner, Context);
        <span class="keyword">return </span>result.GetResult(Context);
    }

    <span class="keyword">private </span><span class="identifier type">ParserFunc</span>&lt;T&gt; FinalFuture(T value)
    {
        <span class="keyword">return </span>(scanner, context) =&gt; context.StopResult(value);
    }
}
</pre>
<p>这个类里我们定义了整个解析器最终的一个 future ——它产生令所有分支判断停止的 StopResult 。这里最关键的是利用 result.GetResult 虚方法推进广度优先的分支选取，并且收集这条路线上所有的语法错误。我们所有的语法错误就只有两种：“丢失某单词”（采用了插入方式错误恢复）和“发现了未预期的某单词”（采用了删除方式错误恢复）。</p>

<p>下面的例子演示了真正的 VBF.Compilers.Parsers.Combinators.dll 库的用法。真正的 VBF 库除了定义基本组合子之外还定义了许许多多的重载和扩展函数，基本实现了 EBNF 的所有功能（而且还可以很容易地继续无限扩展）。用 VBF 库时 Linq 语句可以直接在 Token 上使用，而无需到处使用 AsParser 扩展方法。此外还有大量的代码，限于逻辑无法全部在博客中展现，大家如想了解最好的方法还是直接下载我的代码观看和试用。</p>
<pre class="code mghl cs"><span class="keyword">class </span><span class="identifier type">Node
</span>{
    <span class="keyword">public </span><span class="identifier type">Node </span>LeftChild { <span class="keyword">get</span>; <span class="keyword">private set</span>; }
    <span class="keyword">public </span><span class="identifier type">Node </span>RightChild { <span class="keyword">get</span>; <span class="keyword">private set</span>; }
    <span class="keyword">public char </span>Label { <span class="keyword">get</span>; <span class="keyword">private set</span>; }

    <span class="keyword">public </span>Node(<span class="keyword">char </span>label, <span class="identifier type">Node </span>left, <span class="identifier type">Node </span>right)
    {
        Label = label;
        LeftChild = left;
        RightChild = right;
    }
}

<span class="keyword">class </span><span class="identifier type">Program
</span>{
    <span class="keyword">static void </span>Main(<span class="keyword">string</span>[] args)
    {
        <span class="identifier type">Lexicon </span>binaryTreeSyntax = <span class="keyword">new </span><span class="identifier type">Lexicon</span>();
        <span class="identifier type">LexerState </span>lex = binaryTreeSyntax.DefaultLexer;

        <span class="comment">//定义词法
        </span><span class="identifier type">Token </span>LEFTPH = lex.DefineToken(<span class="identifier type">RE</span>.Symbol(<span class="string">'('</span>));
        <span class="identifier type">Token </span>RIGHTPH = lex.DefineToken(<span class="identifier type">RE</span>.Symbol(<span class="string">')'</span>));
        <span class="identifier type">Token </span>COMMA = lex.DefineToken(<span class="identifier type">RE</span>.Symbol(<span class="string">','</span>));
        <span class="identifier type">Token </span>LETTER = lex.DefineToken(<span class="identifier type">RE</span>.Range(<span class="string">'a'</span>, <span class="string">'z'</span>) | <span class="identifier type">RE</span>.Range(<span class="string">'A'</span>, <span class="string">'Z'</span>));

        espan style="color: green">//定义语法
        </span><span class="identifier type">ParserReference</span>&lt;<span class="identifier type">Node</span>&gt; NodeParser = <span class="keyword">new </span><span class="identifier type">ParserReference</span>&lt;<span class="identifier type">Node</span>&gt;();
        NodeParser.Reference =
            (<span class="keyword">from </span>a <span class="keyword">in </span>LETTER
             <span class="keyword">from </span>_1 <span class="keyword">in </span>LEFTPH
             <span class="keyword">from </span>left <span class="keyword">in </span>NodeParser
             <span class="keyword">from </span>_2 <span class="keyword">in </span>COMMA
             <span class="keyword">from </span>right <span class="keyword">in </span>NodeParser
             <span class="keyword">from </span>_3 <span class="keyword">in </span>RIGHTPH
             <span class="keyword">select new </span><span class="identifier type">Node</span>(a.Value[0], left, right))
            | <span class="identifier type">Parsers</span>.Succeed&lt;<span class="identifier type">Node</span>&gt;(<span class="keyword">null</span>);

        <span class="keyword">var </span>builder = <span class="keyword">new </span><span class="identifier type">ForkableScannerBuilder</span>(binaryTreeSyntax.CreateScannerInfo());

        <span class="keyword">string </span>source = <span class="string">"A(B(,),C(,))"</span>;
        <span class="identifier type">SourceReader </span>sr = <span class="keyword">new </span><span class="identifier type">SourceReader</span>(
            <span class="keyword">new </span><span class="identifier type">StringReader</span>(source));


        <span class="identifier type">ForkableScanner </span>scanner = builder.Create(sr);
        <span class="identifier type">CompilationErrorManager </span>errorManager = <span class="keyword">new </span><span class="identifier type">CompilationErrorManager</span>();

        <span class="identifier type">ParserContext </span>context = <span class="keyword">new </span><span class="identifier type">ParserContext</span>(errorManager, 0, 1);
        context.DefineDefaultCompilationErrorInfo(0);

        <span class="keyword">var </span>runner = <span class="keyword">new </span><span class="identifier type">ParserRunner</span>&lt;<span class="identifier type">Node</span>&gt;(NodeParser.SuffixedBy(<span class="identifier type">Parsers</span>.Eos()), context);
        <span class="keyword">var </span>result = runner.Run(scanner);

        <span class="keyword">foreach </span>(<span class="keyword">var </span>error <span class="keyword">in </span>errorManager.Errors.OrderBy(e =&gt; e.ErrorPosition.StartLocation.CharIndex))
        {
            <span class="identifier type">Console</span>.WriteLine(error.ToString());
        }
    }
}
</pre>
<p>注意 from 语句已经可以直接使用 Token 类型， Union 操作也可以用“|”运算符代替。由于广度优先分支判断的缘故，整个文法在用于解析之前，必须在后面连接一个 EndOfStream ，代表解析到文件末尾才算结束。最后的代码还演示了如何将解析错误打印出来。大家可以将输入字符串故意改错，看看是否能够检测出来。还可以试试错误太多太离谱时的性能下降现象。</p>

<p>在下一篇，我们将正式用这套解析器组合子实现 miniSharp 语言的语法分析器，并且还会接触到 VBF 库扩展组合子的各种用法。敬请期待！</p>
<p>希望大家继续关注我的 VBF 项目：<a href="https://github.com/Ninputer/VBF">https://github.com/Ninputer/VBF</a> 和我的微博：<a href="http://weibo.com/ninputer">http://weibo.com/ninputer</a> 多谢大家支持！</p>
]]></content:encoded>
			<wfw:commentRss>http://typeof.net/2011/07/compiler-by-myself-9/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript: 死或新生</title>
		<link>http://typeof.net/2011/06/javascript-dead-and-alive/</link>
		<comments>http://typeof.net/2011/06/javascript-dead-and-alive/#comments</comments>
		<pubDate>Thu, 30 Jun 2011 13:37:05 +0000</pubDate>
		<dc:creator>Belleve Invis</dc:creator>
				<category><![CDATA[Browser]]></category>
		<category><![CDATA[Discussion]]></category>
		<category><![CDATA[Translation]]></category>
		<category><![CDATA[中文]]></category>
		<category><![CDATA[CoffeeScript]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[translation]]></category>

		<guid isPermaLink="false">http://typeof.net/?p=298</guid>
		<description><![CDATA[原作：JavaScript is Dead. Long Live JavaScript! 翻译： Belleve Invis 此翻译非直译，可能有部分内容省略，为交流用。如有错漏请回复。 序 十六年来， JavaScript 已经完全占领浏览器。它，见证了网页应用（Web Application）的崛起。尽管其他的脚本语言也能担此重任，然而，命运选择了 JavaScript。尽管微软在数年前就在浏览器里加入了 Basic ，但， JavaScript 因为能在所有浏览器中运行，所以它赢了。因能在所有浏览器中运行，自身的质量又并不差，那些浏览器制造商就没有义务给他们的产品增加新语言。 那些华丽的特性——比如闭包——让她成为我们这些情人眼里的西施。编程圈子里经常有这样的忏悔——“是，我知道，这玩意不怎么样，但是给她个机会，好吗？你会爱上她的。”给这语言推销绝非难事。 JavaScript 被说成一个太早拿出来的实验品，而我们一直戴着有色眼镜。 2007 年， Steve Yegge 说 JavaScript 会“引爆程序圈”，现在看来，是真的。从那时到现在，基于 JavaScript 的网页应用已经变得更复杂、更大型以及更好用。借其力量， Web 仍然保持繁荣，丝毫不让移动 App。 最近， JavaScript 在服务器端成功发力。 Node.js 平台，利用其非阻塞 I/O，有望解决无数程序员多年的积怨。作为 Node.js 的选择， JavaScript 可能会在服务器端完成无数前人没有完成的任务。 总而言之， JavaScript 早已横扫全球。但是，如果浏览器今天从地球上消失，又有多少 JavaScript 代码会在第二天写出来？或许她会一夜之间被打入冷宫。幸而，浏览器不会消失，而且还会存活很多年。 随着开发规模的膨胀，那些缺陷逐渐显现，日复一日戳痛开发人员的心。你会觉得奇怪，为什么我写 JavaScript 那么多年我还不是 JavaScript 教教徒，我写的绝大多数 [...]]]></description>
			<content:encoded><![CDATA[<p>原作：<a href="http://peter.michaux.ca/articles/javascript-is-dead-long-live-javascript">JavaScript is Dead. Long Live JavaScript!</a></p>
<p>翻译： Belleve Invis</p>
<p>此翻译非直译，可能有部分内容省略，为交流用。如有错漏请回复。</p>
<h2>序</h2>
<p>十六年来， JavaScript 已经完全占领浏览器。它，见证了网页应用（Web Application）的崛起。尽管其他的脚本语言也能担此重任，然而，命运选择了 JavaScript。尽管微软在数年前就在浏览器里加入了 Basic ，但， JavaScript 因为能在所有浏览器中运行，所以它赢了。因能在所有浏览器中运行，自身的质量又并不差，那些浏览器制造商就没有义务给他们的产品增加新语言。</p>
<p>那些华丽的特性——比如闭包——让她成为我们这些情人眼里的西施。编程圈子里经常有这样的忏悔——“是，我知道，这玩意不怎么样，但是给她个机会，好吗？你会爱上她的。”给这语言推销绝非难事。 JavaScript 被说成一个太早拿出来的实验品，而我们一直戴着有色眼镜。</p>
<p>2007 年， Steve Yegge 说 JavaScript 会<a href="http://steve-yegge.blogspot.com/2007/02/next-big-language.html">“引爆程序圈”</a>，现在看来，是真的。从那时到现在，基于 JavaScript 的网页应用已经变得更复杂、更大型以及更好用。借其力量， Web 仍然保持繁荣，丝毫不让移动 App。</p>
<p>最近， JavaScript 在服务器端成功发力。 Node.js 平台，利用其非阻塞 I/O，有望解决无数程序员多年的积怨。作为 Node.js 的选择， JavaScript 可能会在服务器端完成无数前人没有完成的任务。</p>
<p>总而言之， JavaScript 早已横扫全球。但是，如果浏览器今天从地球上消失，又有多少 JavaScript 代码会在第二天写出来？或许她会一夜之间被打入冷宫。幸而，浏览器不会消失，而且还会存活很多年。</p>
<p>随着开发规模的膨胀，那些缺陷逐渐显现，日复一日戳痛开发人员的心。你会觉得奇怪，为什么我写 JavaScript 那么多年我还不是 JavaScript 教教徒，我写的绝大多数 JavaScript 文章都在说我怎么克服重重障碍与之和谐相处的。我很喜欢 JavaScript 编程，但也很多次感觉犹如陷入泥潭。</p>
<p>一个最明显的缺陷——能直接看到的——就是<strong>语法</strong>。这问题不解决，它迟早要倒台。</p>]]></content:encoded>
			<wfw:commentRss>http://typeof.net/2011/06/javascript-dead-and-alive/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>自己动手开发编译器（八）用Linq编写解析器组合子</title>
		<link>http://typeof.net/2011/06/compiler-by-myself-8/</link>
		<comments>http://typeof.net/2011/06/compiler-by-myself-8/#comments</comments>
		<pubDate>Mon, 27 Jun 2011 08:59:34 +0000</pubDate>
		<dc:creator>Belleve Invis</dc:creator>
				<category><![CDATA[Depth]]></category>
		<category><![CDATA[dotNET/mono]]></category>
		<category><![CDATA[中文]]></category>
		<category><![CDATA[compiler]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[parser]]></category>
		<category><![CDATA[syntax]]></category>
		<category><![CDATA[~ninputer]]></category>

		<guid isPermaLink="false">http://typeof.net/?p=295</guid>
		<description><![CDATA[上回我们说到手写递归下降语法分析器。手写递归下降的方式是目前很多编译器采用的方式，如果你想写一个商业质量的编译器，这是首选的方法。但是，一个完善的递归下降解析器需要的代码量也不少，如果要进行错误报告、错误恢复等等那代码量就更大了。作为懒人，我们有时想要一些小型语言的解析器，最好写起来像直接写文法的产生式一样，最好连错误报告和错误恢复也一并自动解决，可能吗？在过去很长一段时间，人们采用的方法是使用解析器生成器（parser generator）。因为不管是 LL 递归下降解析还是 LR 的移进归约解析，都可以很容易地用计算机来生成所需的规则。这样的著名工具有 yacc、 ANTLR 等。他们的特点是要用一种专门的语法格式来编写文法产生式，然后经过一个翻译程序生成解析器的代码。在函数式语言发展起来之后，有些人发现函数式语言的抽象能力非常强，甚至能够直接用函数式语言的代码来表达文法的产生式，并将解析器“组合”出来，这称作解析器组合子（parser combinator）。如今 C# 和 VB 语言也具有函数式语言相当的特征，特别是还有 Linq 助阵，以至于在 C# 和 VB 中也能享受组合子带来的方式。今天我们就来看看怎么做解析器的组合子。这一篇文字描述可能比较模糊，大家一定要认真地看代码，动手实验。 解析器组合子的基本思想是“组合”，首先我们要定义一些最基本的产生式作为基础组合子，然后通过组合的方式拼装出最终的解析器来。回想一下正则表达式的定义，它有两个基本表达式要素——空表达式和字符表达式，以及三个基本运算——并、连接和克林闭包。用基本运算连接基本表达式，就能组成任何正则表达式。解析器组合子也需要定义两个基本的产生式和两个基本运算。 首先是产生空字符的产生式：G &#8594; &#949;; 这个产生式不产生任何单词，换句话说在解析的时候，不解析任何单词就能成功解析出一个 G。因此这个产生式的解析器永远都能解析成功。 接下来是产生一个单词 t 的产生式：G &#8594; t; 产生式产生一个特定单词 t，表示在解析的时候，如果遇到单词 t，则成功解析出一个 G，而遇到其他单词则会解析失败。 再来定义两个基本运算。首先是连接运算：G &#8594; X Y; 产生式先产生 X 再产生 Y，表示在解析的时候先成功解析 X，再成功解析 Y，就能成功解析出一个 G。 接下来是并运算：G → X; G → Y; 这表示，G 既可以产生 X [...]]]></description>
			<content:encoded><![CDATA[<p>上回我们说到手写递归下降语法分析器。手写递归下降的方式是目前很多编译器采用的方式，如果你想写一个商业质量的编译器，这是首选的方法。但是，一个完善的递归下降解析器需要的代码量也不少，如果要进行错误报告、错误恢复等等那代码量就更大了。作为懒人，我们有时想要一些小型语言的解析器，最好写起来像直接写文法的产生式一样，最好连错误报告和错误恢复也一并自动解决，可能吗？在过去很长一段时间，人们采用的方法是使用解析器生成器（parser generator）。因为不管是 LL 递归下降解析还是 LR 的移进归约解析，都可以很容易地用计算机来生成所需的规则。这样的著名工具有 yacc、 ANTLR 等。他们的特点是要用一种专门的语法格式来编写文法产生式，然后经过一个翻译程序生成解析器的代码。在函数式语言发展起来之后，有些人发现函数式语言的抽象能力非常强，甚至能够直接用函数式语言的代码来表达文法的产生式，并将解析器“组合”出来，这称作解析器组合子（parser combinator）。如今 C# 和 VB 语言也具有函数式语言相当的特征，特别是还有 Linq 助阵，以至于在 C# 和 VB 中也能享受组合子带来的方式。今天我们就来看看怎么做解析器的组合子。这一篇文字描述可能比较模糊，大家一定要认真地看代码，动手实验。</p><span id="more-295"></span>
<p>解析器组合子的基本思想是“组合”，首先我们要定义一些最基本的产生式作为基础组合子，然后通过组合的方式拼装出最终的解析器来。回想一下正则表达式的定义，它有两个基本表达式要素——空表达式和字符表达式，以及三个基本运算——并、连接和克林闭包。用基本运算连接基本表达式，就能组成任何正则表达式。解析器组合子也需要定义两个基本的产生式和两个基本运算。</p>
<p>首先是产生空字符的产生式：</p><pre class="algorithm"><i>G</i> &rarr; <b>&epsilon;</b>;</pre>
<p>这个产生式不产生任何单词，换句话说在解析的时候，不解析任何单词就能成功解析出一个 <i>G</i>。因此这个产生式的解析器永远都能解析成功。</p>
<p>接下来是产生一个单词 t 的产生式：</p><pre class="algorithm"><i>G</i> &rarr; t;</pre>
<p>产生式产生一个特定单词 t，表示在解析的时候，如果遇到单词 t，则成功解析出一个 <i>G</i>，而遇到其他单词则会解析失败。   再来定义两个基本运算。首先是连接运算：</p><pre class="algorithm"><i>G</i> &rarr; <i>X</i> <i>Y</i>;</pre>
<p>产生式先产生 <i>X</i> 再产生 <i>Y</i>，表示在解析的时候先成功解析 <i>X</i>，再成功解析 <i>Y</i>，就能成功解析出一个 <i>G</i>。</p>
<p>接下来是并运算：</p><pre class="algorithm"><i>G</i> → <i>X</i>;
<i>G</i> → <i>Y</i>;</pre>
<p>这表示，<i>G</i> 既可以产生 <i>X</i> 也可以产生 <i>Y</i>。在解析时无论成功解析 <i>X</i> 还是 <i>Y</i>，都能成功解析出一个 <i>G</i>。以上四种基本产生式嵌套使用，就能表示任何上下文无关文法。</p>
<p>下面定义解析器函数的原型委托：</p><pre>public delegate IResult&lt;T&gt; ParserFunc&lt;out T&gt;(ForkableScanner scanner);

public interface IResult&lt;out T&gt;
{
    T Value { get; }
    ForkableScanner ReturnedScanner { get; }
}

public class Result&lt;T&gt; : IResult&lt;T&gt;
{
    public T Value { get; private set; }
    public ForkableScanner ReturnedScanner { get; private set; }

    public Result(T value, ForkableScanner returnedScanner)
    {
        Value = value;
        ReturnedScanner = returnedScanner;
    }
}
</pre>
<p>这并不是 VBF.Compilers.Parsers.Combinators 库最后采用的 Parser 函数原型，但它非常适合第一次接触解析器组合子的同学们理解。先看第一行委托的结构，它接受一个 ForkableScanner 作为参数，然后返回一个 IResult<T>类型。首先什么是 ForkableScanner 呢？我们在词法分析篇定义的 Scanner 类只能不断地向前 Read，而在函数式编程风格中，我们需要一个无副作用的 Scanner。简而言之，任何一个个 ForkableScanner 可以随时“Fork”成两个 ForkableScanner，而这两个 Scanner 任何一个向前扫描，都不会影响另外一个，而且他们各自扫描都回得到同样的单词流。这都是为了处理上述“并”运算的解析器，并运算需要两个分支能够互不影响地单独进行。接下来是返回类型 IResult<T>，定义成接口是为了能够加上.NET 4 泛型协变的“out”关键字。实际类型 Result<T>包含一个解析结果 T 和成功解析之后返回的 Scanner，代表余下的输入流。如果返回的整个 Result 对象为 null，则表示解析失败。后面所有解析器组合子最终都是为了生成这样一个委托的对象，一旦生成了这个对象，就可以马上拿来解析了。</p>
<p>有了解析器函数原型，下面就开始一样一样地定义基础组合子。所谓组合子其实都是一些静态方法（本例中这些静态方法都定义在 Parsers 静态类中）、返回类型就是上面的解析器委托。由于返回类型也是委托，所以这些组合子实际上都是一些高阶函数（返回函数的函数）。在我们的代码中常常是一个 lambda 表达式。较少使用 lambda 表达是的同学第一次看下面的代码可能会略微感到头晕，只需要稍微休息一下再重新看即可……</p>
<p>首先是空产生式 <i>G</i> → <b>ε</b>，它的组合子是：</p><pre>public static ParserFunc&lt;T&gt; Succeed&lt;T&gt;(T result)
{
    return scanner =&gt; new Result&lt;T&gt;(result, scanner);
}
</pre>
<p>这个组合子接受一个参数，表示其解析结果。正如前面所介绍，由 Succeed 组合子生成的解析器，永远都会成功解析，而且会将设定的结果返回。</p>
<p>第二种是接受一个单词的的产生式 <i>G</i> &rarr; t，我们将它的组合子定义成一个扩展方法：</p><pre>public static ParserFunc&lt;Lexeme&gt; AsParser(this Token token)
{
    return scanner =&gt;
    {
        var lexeme = scanner.Read();
        if (lexeme.TokenIndex == token.Index)
        {
            return new Result&lt;Lexeme&gt;(lexeme, scanner);
        }
        else
        {
            return null;
        }
    };
}
</pre>
<p>注意这个组合子生成的解析器是 Lexeme（词素）类型的，词素对象是我们在词法分析阶段定义的，里面包含了词素的类型和具体字符串。这个组合子接受一个 Token 作为参数，而返回的解析器从输入的 Scanner 中读取下一个词素，如果该词素的单词类型与传入的 Token 相匹配，就报告解析成功，否则解析失败。</p>
<p>第三种是两个文法的连接 <i>G</i> → <i>X</i> <i>Y</i>。我们需要定义一个组合子，接受两个已经存在的 ParserFunc 函数，返回一个新的 ParserFunc，先后调用两个传入的 ParserFunc：</p><pre>public static ParserFunc&lt;Tuple&lt;T1, T2&gt;&gt; Concat&lt;T1, T2&gt;
        (this ParserFunc&lt;T1&gt; parser1, ParserFunc&lt;T2&gt; parser2)
{

    return scanner =&gt;
    {
        var result1 = parser1(scanner);

        if (result1 == null) return null;

        var result2 = parser2(result1.ReturnedScanner);

        if (result2 == null) return null;

        return new Result&lt;Tuple&lt;T1, T2&gt;&gt;(
            Tuple.Create(result1.Value, result2.Value), result2.ReturnedScanner);
    };
}
</pre>
<p>注意我们返回的 ParserFunc 结果类型是  <code>Tuple&lt;T1, T2&gt;</code> ，因为结果中需要同时包含 T1 和 T2。</p>
<p>用这种方式定义的连接运算组合子，在实践中非常难用。因为我们的文法常常要包含不止两个符号连接的情形。假如我们的产生式是 <i>G</i> → <i>X</i> <i>Y</i> <i>Z</i>，那么必须写成  <code>X.Concat(Y.Concat(Z))</code> ，而它的返回类型是  <code>Tuple&lt;T1, Tuple&lt;T2, T3&gt;&gt;</code> ，如果要取得结果中的 Z，只能写 r.Item2.Item2。实际上 miniSharp 这样的语言，文法中出现 7-8 个符号连接也不是什么稀奇的事情，而如果都用这个组合子的话， Tuple 嵌套会复杂到把人的眼睛都搞晕掉。所以这时我们想到了——Linq。 Linq 的“组合子”中，有一种叫 SelectMany，他给我们带来了这种语法糖：</p><pre>List&lt;int&gt; la = new List&lt;int&gt;() { 1, 2, 3 };
List&lt;string&gt; lb = new List&lt;string&gt;() { "a", "b", "c" };

var r = from a in la
        from b in lb
        select a + b;
</pre>
<p>它实际可以翻译成：</p><pre>var r = la.SelectMany(a =&gt; lb.SelectMany(b =&gt; a + b));
</pre>
<p>也就是说，连续 from 语句，其实是 SelectMany 扩展方法的嵌套调用。这种调用方法有把 lambda 嵌套“打平”的功能，非常类似于单子风格中的 Bind 运算。实际上 C# 和 VB 允许在任何自定义类型上扩展 SelectMany 方法，然后就允许用 Linq 语法的 from 去调用。有些人非常鄙视语法糖，但这个语法糖却是无法替代的，这是 C# 版解析器组合子关键中的关键！由此就可以将连接运算定义成一个 SelectMany 组合子：</p><pre>public static ParserFunc&lt;TResult&gt; SelectMany&lt;T1, T2, TResult&gt;(this ParserFunc&lt;T1&gt; parser,
    Func&lt;T1, ParserFunc&lt;T2&gt;&gt; parserSelector, Func&lt;T1, T2, TResult&gt; resultSelector)
{

    return scanner =&gt;
    {
        var result1 = parser(scanner);

        if (result1 == null) return null;

        var parser2 = parserSelector(result1.Value);

        var result2 = parser2(result1.ReturnedScanner);

        if (result2 == null) return null;

        return new Result&lt;TResult&gt;(
            resultSelector(result1.Value, result2.Value), result2.ReturnedScanner);
    };
}
</pre>
<p>这个神奇的 SelectMany 组合子不但消除了嵌套 Tuple 带来的混乱，还允许我们用一个自定义的 select 语句生成连接运算的结果，这在生成语法树的时候尤为方便。我们一会再看例子，先继续看最后一种基本组合子。</p>
<p>最后一种基本组合子是并运算。并运算要求产生式产生两种可能的分支。对应到解析器组合子上，连接运算也要接受两个现成的解析器作为参数，但是选择哪一个呢？这里我们没有办法做分支预测，所以只好采取尝试的办法。有一种尝试的方法就是先试用第一个解析器，如果失败了，再试用第二个，这是一种类似深度优先搜索的办法：</p><pre>public static ParserFunc&lt;T&gt; Union&lt;T&gt;(this ParserFunc&lt;T&gt; parser1, ParserFunc&lt;T&gt; parser2)
{
    return scanner =&gt;
    {
        var scanner1 = scanner;
        var scanner2 = scanner.Fork();

        var result1 = parser1(scanner1);
        if (result1 != null)
        {

            return result1;
        }

        var result2 = parser2(scanner2);

        return result2;
    };
}
</pre>
<p>仅仅使用以上四个组合子函数，就可以来写 Parser 了！是否还半信半疑呢？我们就来写上一次写过的二叉树字符串表示的语法分析器。忘记的同学建议打开上一篇看看。我们把文法再抄一遍：</p><pre class="algorithm"><i>N</i> → a ( <i>N</i>, <i>N</i> )
<i>N</i> → <b>ε</b></pre>
<p>这里面涉及的单词包括字母、左右括号和逗号，我们都用词法分析篇的方法将他们定义出来。然后再用解析器组合子组合出上述文法的解析器。完整的代码如下：</p><pre>Lexicon binaryTreeSyntax = new Lexicon();
LexerState lex = binaryTreeSyntax.DefaultLexer;

//定义词法
Token LEFTPH = lex.DefineToken(RE.Symbol('('));
Token RIGHTPH = lex.DefineToken(RE.Symbol(')'));
Token COMMA = lex.DefineToken(RE.Symbol(','));
Token LETTER = lex.DefineToken(RE.Range('a','z') | RE.Range('A','Z'));

//定义语法
ParserFunc&lt;Node&gt; NodeParser = null;
NodeParser = 
    (from a in LETTER.AsParser()
     from _1 in LEFTPH.AsParser()
     from left in NodeParser
     from _2 in COMMA.AsParser()
     from right in NodeParser
     from _3 in RIGHTPH.AsParser()
     select new Node(a.Value[0], left, right))
    .Union(Parsers.Succeed&lt;Node&gt;(null));


//运行部分
ForkableScannerBuilder builder = 
    new ForkableScannerBuilder(binaryTreeSyntax.CreateScannerInfo());

string source = "A(B(,),C(,))";
SourceReader sr = new SourceReader(
    new StringReader(source));

ForkableScanner scanner = builder.Create(sr);

var tree = NodeParser(scanner);
</pre>
<p>重点来看“定义语法”部分，我们来看看产生式都是如何转变为组合子调用的。首先，<i>N</i> → <b>ε</b> 转化为了一句  <code>Parsers.Succeed</code>  调用，代表总能解析成功，而且不消耗输入单词的解析器。然后是 <i>N</i> → a ( <i>N</i>, <i>N</i> )，连续的连接转化为一连串 Linq 的 from 子句。而其中出现了终结符的地方，则通过 AsParser 扩展方法将 Token 转化为 Parser。最后再用一个 Union 组合子将两个 N 产生式组合到一起，中间我们还看到了用 select 子句方便地构造想要的解析结果能力。再一次，赞叹 SelectMany 的神奇力量！初看起来， Linq 用来写文法感觉怪怪的，但是习惯了之后，可以非常快速地将各种产生式以 Linq 语句的方式表达出来。</p>
<p>解析器组合子最大的优点就是无论实现还是使用都非常简洁，高度体现了函数式编程的优势。但它最大的缺点是难以调试。倘若大家用解析器组合子组合出来的解析器有错误，无法获得想要的解析结果，那可就麻烦了。大家可以试试用 Visual Studio 的调试器跟踪一下解析器组合子，会发现它的跳转非常频繁，而且根本看不出当前在干什么（因为运行时已经生成了 Lambda 函数，无法获得组合子传入的参数），也无法看出下一步会运行什么。所以，采用解析器组合子唯一确保正确的做法，就是编写足够的测试用例。</p>
<p>还有一个重要的问题要解决——语法错误。大家可以试一试输入一个不符合语法的字符串，比如去掉一个括号，看看会是什么结果？答案是直接返回 null——和一开始的设定一样。无法知道错误出在了哪里。作为编程语言的解析器，不仅应该能报告错误出现的位置，而且还应该能自动进行某种错误恢复，这样就可以继续完成解析，从而获得所有的语法错误，而不仅仅是头一个。这个功能非常重要，但我们今天设计的解析器组合子结构却非常不擅长进行错误报告和恢复。比如说 Union 组合子，干脆就是通过解析错误来判断要不要采用这个分支，如果每个分支都错了，它又如何决定报告哪条分支的错误呢？可以设定一些规则，但是我们想要更好、更智能的错误报告和恢复功能。这就留到下一篇，正式介绍 VBF 库中采用的 CPS 风格解析器组合子了。敬请期待！</p>
<p>希望大家继续关注我的 VBF 项目： http://github.com/Ninputer/VBF 和我的微博： http://weibo.com/ninputer 多谢大家支持！</p>]]></content:encoded>
			<wfw:commentRss>http://typeof.net/2011/06/compiler-by-myself-8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>自己动手开发编译器（七）递归下降的语法分析器</title>
		<link>http://typeof.net/2011/06/compiler-by-myself-7/</link>
		<comments>http://typeof.net/2011/06/compiler-by-myself-7/#comments</comments>
		<pubDate>Fri, 24 Jun 2011 17:04:34 +0000</pubDate>
		<dc:creator>Belleve Invis</dc:creator>
				<category><![CDATA[Depth]]></category>
		<category><![CDATA[dotNET/mono]]></category>
		<category><![CDATA[中文]]></category>
		<category><![CDATA[compiler]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[parser]]></category>
		<category><![CDATA[syntax]]></category>

		<guid isPermaLink="false">http://typeof.net/?p=287</guid>
		<description><![CDATA[上回我们说到语法分析使用的上下文无关语言，以及描述上下文无关文法的产生式、产生式推导和语法分析树等概念。今天我们就来讨论实际编写语法分析器的方法。今天介绍的这种方法叫做递归下降（recursive descent）法，这是一种适合手写语法编译器的方法，且非常简单。递归下降法对语言所用的文法有一些限制，但递归下降是现阶段主流的语法分析方法，因为它可以由开发人员高度控制，在提供错误信息方面也很有优势。就连微软C#官方的编译器也是手写而成的递归下降语法分析器。使用递归下降法编写语法分析器无需任何类库，编写简单的分析器时甚至连前面学习的词法分析库都无需使用。我们来看一个例子：现在有一种表示二叉树的字符串表达式，它的文法是： N → a ( N, N )N → ε 其中终结符a表示任意一个英文字母，ε表示空。这个文法的含义是，二叉树的节点要么是空，要么是一个字母开头，并带有一对括号，括号中逗号左边是这个节点的左儿子，逗号右边是这个节点的右儿子。例如字符串 A(B(,C(,)),D(,))就表示这样一棵二叉树： 注意，文法规定节点即使没有儿子（儿子是空），括号和逗号也是不可省略的，所以只有一个节点的话也要写成A(,)。现在我们要写一个解析器，输入这种字符串，然后在内存中建立起这棵二叉树。其中内存中的二叉树是用下面这样的类来表示的： classNode { public Node LeftChild { get; private set; } public Node RightChild { get; private set; } public char Label { get; private set; } public Node(char label, Node left, Node right) { Label = label; LeftChild = left; RightChild [...]]]></description>
			<content:encoded><![CDATA[<p>上回我们说到语法分析使用的上下文无关语言，以及描述上下文无关文法的产生式、产生式推导和语法分析树等概念。今天我们就来讨论实际编写语法分析器的方法。今天介绍的这种方法叫做<strong>递归下降</strong>（recursive descent）法，这是一种适合手写语法编译器的方法，且非常简单。递归下降法对语言所用的文法有一些限制，但递归下降是现阶段主流的语法分析方法，因为它可以由开发人员高度控制，在提供错误信息方面也很有优势。就连微软C#官方的编译器也是手写而成的递归下降语法分析器。</p><span id="more-287"></span><p>使用递归下降法编写语法分析器无需任何类库，编写简单的分析器时甚至连前面学习的词法分析库都无需使用。我们来看一个例子：现在有一种表示二叉树的字符串表达式，它的文法是：</p> <pre>N → <font color="#0000ff">a (</font> N<font color="#0000ff">,</font> N <font color="#0000ff">)<br /></font>N → <font color="#0000ff">ε</font></pre> <p>其中终结符a表示任意一个英文字母，<font color="#000000">ε</font>表示空。这个文法的含义是，二叉树的节点要么是空，要么是一个字母开头，并带有一对括号，括号中逗号左边是这个节点的左儿子，逗号右边是这个节点的右儿子。例如字符串 <strong>A(B(,C(,)),D(,))</strong>就表示这样一棵二叉树：</p> <p><a href="http://images.cnblogs.com/cnblogs_com/Ninputer/201106/201106202147104765.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="bintree" border="0" alt="bintree" src="http://images.cnblogs.com/cnblogs_com/Ninputer/201106/201106202147113818.png" width="184" height="251"></a></p> <p>注意，文法规定节点即使没有儿子（儿子是空），括号和逗号也是不可省略的，所以只有一个节点的话也要写成<strong>A(,)</strong>。现在我们要写一个解析器，输入这种字符串，然后在内存中建立起这棵二叉树。其中内存中的二叉树是用下面这样的类来表示的：</p> <pre><span style="color:blue">class</span><span style="color: #2b91af">Node
</span>{
    <span style="color: blue">public </span><span style="color: #2b91af">Node </span>LeftChild { <span style="color: blue">get</span>; <span style="color: blue">private set</span>; }
    <span style="color: blue">public </span><span style="color: #2b91af">Node </span>RightChild { <span style="color: blue">get</span>; <span style="color: blue">private set</span>; }
    <span style="color: blue">public char </span>Label { <span style="color: blue">get</span>; <span style="color: blue">private set</span>; }

    <span style="color: blue">public </span>Node(<span style="color: blue">char </span>label, <span style="color: #2b91af">Node </span>left, <span style="color: #2b91af">Node </span>right)
    {
        Label = label;
        LeftChild = left;
        RightChild = right;
    }
}

</pre>

<p>这是一道微软面试题，曾经难倒了不少参加面试的候选人。不知在座各位是否对写出这段程序有信心呢？不少参选者想到了要用栈，或者用递归，去寻找逗号的位置将字符串拆解开来等等方法。但是若是使用递归下降法，这个程序写起来非常容易。我们来看看编写递归下降语法分析器的一般步骤：</p>

<ol>

<li>使用一个索引来记录当前扫描的位置。通常将它做成一个整数字段。 

<li>为每个非终结符编写一个方法。 

<li>如果一个非终结符有超过一个的产生式，则在这个方法中对采用哪个产生式进行<strong>分支预测</strong>。 

<li>处理单一产生式时，遇到正确终结符则将第一步创建的扫描索引位置向前移动；如遇到非终结符则调用第二步中创建的相应方法。 

<li>如果需要产生解析的结果（比如本例中的二叉树），在方法返回之前将它构造出来。</li></ol>

<p>我们马上来试验一下。首先建立一个类，然后存放一个索引变量来保存当前扫描位置。然后要为每一个非终结符创建一个方法，我们的文法中只有一个非终结符N，所以只需创建一个方法：</p>

<pre class="code"><span style="color: blue">class </span><span style="color: #2b91af">BinaryTreeParser
</span>{
    <span style="color: blue">private string </span>m_inputString;
    <span style="color: blue">private int </span>m_index;

    <span style="color: green">//初始化输入字符串和索引的构造函数，略

    </span><span style="color: #2b91af">Node </span>ParseNode()
    {
    }
}</pre>

<p>回到刚才的产生式，我们看到非终结符N有两个产生式，所以在ParseNode方法的一开始我们必须做出分支预测。分支预测的方法是<strong>超前查看</strong>（look ahead）。就是说我们先“偷窥”当前位置前方的字符，然后判断应该用哪个产生式继续分析。非终结符N的两个产生式其中一个会产生a(N, N)这个的结构，而另一个则直接产生空字符串。那现在知道，起码有一种可能就是会遇到一个字母，这时候应该采用N → a(N, N)这个产生式继续分析。那么什么时候应该采用N → ε进行分析呢？我们观察产生式右侧所有出现N的地方，倘若N是空字符串，那么N后面的字符就会直接出现，也就是逗号和右括号。于是这就是我们的分支预测：</p>

<ol>

<li>如果超前查看遇到英文字母，预测分支N → a(N, N) 

<li>如果超前查看遇到逗号、右括号预测分支N → ε</li></ol>

<p>转化成代码就是这样：</p>

<pre class="code"><span style="color: #2b91af">Node </span>ParseNode()
{
    <span style="color: blue">int </span>lookAheadIndex = m_index;

    <span style="color: blue">char </span>lookAheadChar = m_inputString[lookAheadIndex];

    <span style="color: blue">if </span>(<span style="color: #2b91af">Char</span>.IsLetter(lookAheadChar))
    {
        <span style="color: green">//采用N → a(N, N)继续分析
    </span>}
    <span style="color: blue">else if </span>(lookAheadChar == <span style="color: #a31515">',' </span>|| lookAheadChar == <span style="color: #a31515">')' </span>)
    {
        <span style="color: green">//采用N → ε继续分析
    </span>}
    <span style="color: blue">else
    </span>{
        <span style="color: blue">throw new </span><span style="color: #2b91af">Exception</span>(<span style="color: #a31515">"语法错误"</span>);
    }
}</pre>

<p>接下来我们分别来看两个分支怎么处理。先来看N → ε，这种情况下非终结符是个空字符串，所以我们不需要移动当前索引，直接返回null表示空节点。再来看N → a(N, N) 分支，倘若输入的字符串没有任何语法错误，那就应该依次遇到字母、左括号、N、逗号、N右括号。根据上面的规则，凡是遇到终结符，就移动当前索引，直接向前扫描；而要是遇到非终结符，就递归调用相应节点的方法。所以（不考虑语法错误）的完整方法代码如下：</p>

<pre class="code"><<span style="color: #2b91af">Node </span>ParseNode()
{
    <span style="color: blue">int </span>lookAheadIndex = m_index;

    <span style="color: blue">char </span>lookAheadChar = m_inputString[lookAheadIndex];

    <span style="color: blue">if </span>(<span style="color: #2b91af">Char</span>.IsLetter(lookAheadChar))
    {
        <span style="color: green">//采用N → a(N, N)继续分析
        </span><span style="color: blue">char </span>label = m_inputString[m_index++]; <span style="color: green">//解析字母
        </span>m_index++; <span style="color: green">//解析左括号，因为不需要使用它的值，所以直接跳过

        </span><span style="color: #2b91af">Node </span>left = ParseNode(); <span style="color: green">//非终结符N，递归调用

        </span>m_index++; <span style="color: green">//解析逗号，跳过

        </span><span style="color: #2b91af">Node </span>right = ParseNode(); <span style="color: green">//非终结符N，递归调用

        </span>m_index++; <span style="color: green">//解析右括号，跳过

        </span><span style="color: blue">return new </span><span style="color: #2b91af">Node</span>(label, left, right);
    }
    <span style="color: blue">else if </span>(lookAheadChar == <span style="color: #a31515">',' </span>|| lookAheadChar == <span style="color: #a31515">')'</span>)
    {
        <span style="color: green">//采用N → ε继续分析
        //无需消耗输入字符，直接返回null
        </span><span style="color: blue">return null</span>;
    }
    <span style="color: blue">else
    </span>{
        <span style="color: blue">throw new </span><span style="color: #2b91af">Exception</span>(<span style="color: #a31515">"语法错误"</span>);
    }
}</pre>

<p>因为存在语法约束，所以一旦我们完成了分支预测，就能清楚地知道下一个字符或非终结符一定是什么，无需再进行任何判断（除非要进行语法错误检查）。因此根本就不需要寻找逗号在什么位置，我们解析到逗号时，逗号一定就在那，这种感觉是不是很棒？只需要寥寥几行代码就已经写出了一个完整的Parser。大家感兴趣可以继续补全一些辅助代码，然后用真正的字符串输入试验一下，是否工作正常。前面假设输入字符串的语法是正确的，但真实世界的程序总会写错，所以编译器需要能够帮助检查语法错误。在上述程序中加入语法错误检查非常容易，只要验证每个位置的字符，是否真的等于产生式中规定的终结符就可以了。这就留给大家做个练习吧。</p>

<p>上面我们采用的分支预测法是“人肉观察法”，编译原理书里一般都有一些计算FIRST集合或FOLLOW集合的算法，可以算出一个产生式可能开头的字符，这样就可以用自动的方法写出分支预测，从而实现递归下降语法分析器的自动化生成。ANTLR就是用这种原理实现的一个著名工具。有兴趣的同学可以去看编译原理书。其实我觉得“人肉观察法”在实践中并不困难，因为编程语言的文法都特别有规律，而且我们天天用编程语言写代码，都很有经验了。</p>

<p>下面我们要研究一下递归下降法对文法有什么限制。首先，我们必须要通过超前查看进行分支预测。<strong>支持递归下降的文法，必须能通过从左往右超前查看k个字符决定采用哪一个产生式</strong>。我们把这样的文法称作<strong>LL(k)</strong>文法。这个名字中第一个L表示从左往右扫描字符串，这一点可以从我们的index变量从0开始递增的特性看出来；而第二个L表示<strong>最左推导</strong>，想必大家还记得上一篇介绍的最左推导的例子。大家可以用调试器跟踪一遍递归下降语法分析器的分析过程，就能很容易地感受到它的确是最左推导的（总是先展开当前句型最左边的非终结符）。最后括号中的k表示需要超前查看k个字符。如果在每个非终结符的解析方法开头超前查看k个字符不能决定采用哪个产生式，那这个文法就不能用递归下降的方法来解析。比如下面的文法：</p>

<pre>F → id<br />F → ( E )<br />E → F * F<br />E → F / F</pre>

<p>当我们编写非终结符E的解析方法时，需要在两个E产生式中进行分支预测。然而两个E产生式都以F开头，而且F本身又可能是任意长的表达式，无论超前查看多少字符，都无法判定到底应该用乘号的产生式还是除号的产生式。遇到这种情况，我们可以用<strong>提取左公因式</strong>的方法，将它转化为LL(k)的文法：</p>

<pre>F → id<br />F → ( E )<br /><strong>G → * F<br />G → / F<br /></strong>E → F<strong>G</strong></pre>

<p>我们将一个左公因式F提取出来，然后将剩下的部分做成一个新的产生式G。在解析G的时候，很容易进行分支预测。而解析E的时候则无需再进行分支预测了。在实践中，提取左公因式不仅可以将文法转化为LL(k)型，还能有助于减少重复的解析，提高性能。</p>

<p>下面我们来看LL(k)文法的第二个重要的限制——不支持左递归。所谓左递归，就是产生式产生的第一个符号有可能是该产生式本身的非终结符。下面的文法是一个直截了当的左递归例子：</p>

<pre>F → id<br />E → E + F<br />E → F</pre>

<p>这个表达式类似于我们上篇末尾得到的无歧义二元运算符的文法。但这个文法存在左递归：E产生的第一个符号就是E本身。我们想像一下，如果在编写E的递归下降解析函数时，直接在函数的开头递归调用自己，输入字符串完全没有消耗，这种递归调用就会变成一种死循环。所以，左递归是必须要消除的文法结构。解决的方法通常是将左递归转化为等价的右递归形式：</p>

<pre>F → id<br />E → F<strong>G</strong><br /><strong>G → + FG<br />G → <font color="#000000">ε</font></strong></pre>

<p>大家应该牢牢记住这个例子，这不仅仅是个例子，更是解除大部分左递归的万能公式！我们将要在编写miniSharp语法分析器的时候一次又一次地用到这种变换。</p>

<p>由于LL(k)文法不能带有左递归和左公因式，很多常见的文法转化成LL(k)之后显得不是那么优雅。有许多程序员更喜欢使用<strong>LR(k)</strong>文法的语法分析器。LR代表从左到右扫描和最右推导。LR型的文法允许左递归和左公因式，但是并不能用于递归下降的语法分析器，而是要用<strong>移进-归约</strong>型的语法分析器，或者叫<strong>自底向上</strong>的语法分析器来分析。我个人认为LR型语法分析器的原理非常优雅和精妙，但是限于本篇的定位我不准备介绍它。我想任何一本编译原理书里都有详细介绍。当然如果未来我的VBF库支持了LR型语法分析器，我也许会追加一些特别篇，谁知道呢？</p>

<p>希望大家看了今天这篇文章之后，都能用递归下降法写出一些LL(k)文法的语法分析器来。下一篇我将介绍使用C#和VB中神奇的Linq语法来“组合”出语法分析器来，敬请期待！</p>

<p>希望大家继续关注我的VBF项目：<a href="https://github.com/Ninputer/VBF">https://github.com/Ninputer/VBF</a> 和我的微博：<a href="http://weibo.com/ninputer">http://weibo.com/ninputer</a> 多谢大家支持！</p>]]></content:encoded>
			<wfw:commentRss>http://typeof.net/2011/06/compiler-by-myself-7/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

