编辑器制作之语法加亮基本原理在上一篇文章里,我简单的提及了语法加亮的基本思路,下面在总结概括一下。
笔者认为,对于编辑器而言,如果支持非常严格的语法加亮的话,那么扩展性是很低的。那么在扩展性和正确性之间,我们应该取得一个平衡。这个平衡就是既要保证编辑器的高效率运转,又要保持文本配置文件的可编辑性。
首先,几乎所有的编程语言都具有某种共性,这些共性概括如下:
1.关键字
2.注释
3.字符串
4.Delimiters
5.普通字符
那么对于一个字符串序列,我们应该如何做呢?任何一个人都会很自然的想到:从前往后扫描。对,那么如何扫描呢?我用的手段是状态机。或者不能完全称之为状态机,因为在我的状态机里面用到了预先判断,对于一个长度为N的字符串,最坏的情况下会扫描M*N*L次(其中M为某些块的起始或者结束标记的长度,L为块的个数,关于什么是块,参加我的上一篇文章),所以对于我的这个状态机,称之为状态模式更贴切一些。状态模式是个好东西,对于状态模式乃何物以及如何构造,本文不作详细阐述。
如果仅仅是识别上面这些东西的话,那么语法加亮是非常容易实现的。但事与愿违,事情并不是如此简单。举个例子html.在最开始的时候html的确让我伤透了脑筋,因为他可以嵌入各种各样的语言,并且每种语言的schema并不一样,比如可以嵌入css,或者js,或者vbs,当然还有php, java, c#代码等等。这个时候该如何做呢? 我用的手段是分块之后,对于不同的子语言应用不同的schema,这么做并不是完全对的(和Lex分析相比),或多或少会出现某种问题,不过大多数情况下表现的都非常好,这个点就叫平衡。
再说一下状态分析,定义如下函数: 伪代码
//根据起始状态,分析字符串line的第index字符应该是何种状态
state NextState( string line, int index, state start_state ){
switch( start_state ){
case .
return some_state;
case .
return some_state;
case .
return some_state;
case .
return some_state;
}
}
//分析一行字符串的某一个字符应该是何种状态,并预存入cache
state ParseLine( string line, int index, state start_state ){
for( i=index; i<line.Length; i++ ){
start_state = NextState( line, i, start_state );
siwtch( start_state ){
//set text attributes
}
}
//分析完之后,在进行分析一遍,进行一些细节匹配
DetailMatch(...)
//分析完之后,我们要返回该行的最后的状态,用来作为下一行的起始状态
return start_state;
}
//这个函数主要用来对于分完块之后的代码进行细节匹配,比如匹配注释中的email和url
//或者普通字符中的数字等等
void DetailMatch(...){
//use regex to match some details, such as number or email
}
上面这几个函数都简单明了,比较容易理解,对于ParseLine我们发现在进行行跳转的时候DetailMatch并不是必须的。什么叫行跳转呢?比如打开一个代码文件,现在我要跳转到第5000行,那么很显然第5000行需要放到屏幕上头,这个时候我怎么知道第5000行的起始状态呢?当然也得从第一航开始分析,但是我们发现DetailMatch其实并不是必须的,因为我们只需要作块状识别就够了,所以速度是非常客观的。
先写这么多了,等我以后老了,我打算把这些东西写成一本书,名字就叫编辑器制作基本原理,呵呵.
不敢妄自菲薄,下面贴两个代码片段和上面的伪代码均是按照上述方法生成的,还算美观.
C++代码
#include <stdio.h>
// line comment email test@test.com
// url:http://www.cppblog.com/megax in comment over
/*
block comment
email test@test.com url:http://www.cppblog.com/megax in comment over
*/
int main(int argc, char *argv[])
{
email: test@test.com
http://www.cppblog.com/megax in comment over
int a = Class::Somfunction(); // function
char * p = "abcdef
string to new line"; // string can continue, just test
char* p = "abcef\"\\"; //escpae
char* p = 'abcef\"\\'; //escpae, just test;
asm{
; test sub lan
; line comment email test@test.com
; url:http://www.cppblog.com/megax in comment over
mov ax, 10
add ax, 0x12AD
add ax, 123L
jump loop1
}
// number test
int a = 1234; int b = 0xA12D;
int c = 1234L; float a = 123.456;
return 0;
}
HTML代码嵌入css,js
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="images/favicon.ico" rel="SHORTCUT ICON" />
<title>test</title>
<script type="text/javascript">
function setTab(m,n){
var tli=document.getElementById("menu"+m).getElementsByTagName("a");
var mli=document.getElementById("main"+m).getElementsByTagName("div");
for(i=0;i<tli.length;i++){
tli[i].className=i==n?"current1 current2":"";
mli[i].style.display=i==n?"block":"none";
}
var a = 0x012345678;
var a = 0xABCDEF12345;
// line comment test@test.com in comment http://www.cppblog.com/megax in comment over
/*
block comment in js
test@test.com in comment
http://www.cppblog.com/megax in comment
over
*/
}
</script> function style var
<style>
body{
function style var
font-size: 12px;
font-family: "sfdsfdsf";
/*
block comment in css
test@test.com in comment
http://www.cppblog.com/megax in comment
over
*/
}
</style>
</head>
<body>
<table>
</table>
function style var /*sdfdsfdsf*/ return var
<!--
block comment in html
test@test.com in comment
http://www.cppblog.com/megax in comment
over
-->
</body>
</html>
下面看一下cppblog自带的代码加亮,没有c++的,用c#代替
#include <stdio.h>
// line comment email test@test.com
// url:http://www.cppblog.com/megax in comment over
/*
block comment
email test@test.com url:http://www.cppblog.com/megax in comment over
*/
int main(int argc, char *argv[])
{
int a = Class::Somfunction(); // function
char * p = "abcdef
string to new line"; // string can continue, just test
char* p = "abcef\"\\"; //escpae
char* p = 'abcef\"\\'; //escpae, just test;
asm{
; test sub lan
; line comment email test@test.com
; url:http://www.cppblog.com/megax in comment over
mov ax, 10
add ax, 0x12AD
add ax, 123L
jump loop1
}
// number test
int a = 1234; int b = 0xA12D;
int c = 1234L; float a = 123.456;
return 0;
}
HTML的
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="images/favicon.ico" rel="SHORTCUT ICON" />
<title>test</title>
<script type="text/javascript">
function setTab(m,n){
var tli=document.getElementById("menu"+m).getElementsByTagName("a");
var mli=document.getElementById("main"+m).getElementsByTagName("div");
for(i=0;i<tli.length;i++){
tli[i].className=i==n?"current1 current2":"";
mli[i].style.display=i==n?"block":"none";
}
var a = 0x012345678;
var a = 0xABCDEF12345;
// line comment test@test.com in comment http://www.cppblog.com/megax in comment over
/*
block comment in js
test@test.com in comment
http://www.cppblog.com/megax in comment
over
*/
}
</script> function style var
<style>
body{
function style var
font-size: 12px;
font-family: "sfdsfdsf";
/*
block comment in css
test@test.com in comment
http://www.cppblog.com/megax in comment
over
*/
}
</style>
</head>
<body>
<table>
</table>
function style var /*sdfdsfdsf*/ return var
<!--
block comment in html
test@test.com in comment
http://www.cppblog.com/megax in comment
over
-->
</body>
</html>
posted on 2008-07-09 20:23
megax 阅读(2124)
评论(4) 编辑 收藏 引用