2011年8月31日 星期三

[python]reduce的觀念

從python裡,我學到一組對我來說很重要的觀念。
一個是 map,另一個是 reduce。
這兩個對我寫程式的影響很大。
而 reduce 對我來說,真的是比較難理解。
而我最近才比較了解一點點。
在網路上,大多文章談到 reduce,多是舉官方文件的例子:

def sum_num(x,y):
    return x + y
a = [1,2,3,4,5]
b = reduce(sum_num, a)

或者是更簡潔地:

a = [1,2,3,4,5]
b = reduce(lambda x,y : x + y, a)

在我想要做更複雜的事情的時候,腦筋就轉不過來。
難道,只能做數值運算嗎?

讓我們來看一下 reduce 的函數定義,其為

reduce(func, iter, [initial_value])

套用於上面的例子,你可以認為
b = reduce(sum_num, a)
等同於
b = reduce(sum_num, a, 0)

然後,把 reduce(sum_num, a, 0) 想像成如下的函數:

def reduce(func_handler, value_list, init_value):
    b = init_value
    for value in value_list:
        b = func_handler(b, value)
    return b

所以,想像一下,要使用 reduce,要準備三個東西,
(1)一個函數,有兩個傳入值,一個輸出值,這些值,都是屬於同一集合的元素。
(2)一個list,是要被處理的元素。
(3)起始值。

例如,我有一組資料長相如下:
a = [(1,a,3),(2,b,6),(3,c,9),(4,d,12)]
我想要把計算每個tuple的第一個總和、每個tuple的第二個字元要連接、每個tuple的第三個乘積。
也就是要得到
(1+2+3+4,’abcd’,3*6*9*12)
我要準備函數,為了好理解,特別寫成下面的樣子
def sum_special(x, y):
    i0 = x[0]+y[0]
    i1 = x[1]+y[1] #字串相加
    i2 = x[2]*y[2]
    return (i0, i1, i2)
要處理的 list 就是 a,
起始值就是 (0, ‘’, 0) 了。

reduce 這樣用就對了。

當你的程式看到很多次的

a = func(a, b)
a = func(a, c)
a = func(a, d)

或是

b = init_value
for value in value_list:
    b = func_handler(b, value)

那就轉用 reduce 吧。

2011年8月29日 星期一

[python]自己寫一個dict

當寫程式到後來,一定會想要自己寫一個物件來用。
而且,希望能跟 build-in 的物件一樣的使用。
舉例來說,我們想要一個 ordered dict,在 iteration 的時候,
能按照我們加進去的順序列出。

這個 dict 據說在 python 3 已經有了,
這裡只是練習一下該怎麼寫這一類的物件。
這方面的資料在官方文件的
The Python Language Reference >> 3. Data model

首先,要能夠使用 a[‘1a’] = “a” 這個語法,
物件要提供 def __setitem__(self,key,value)。
要能夠使用 b = a[‘1a’] 這個語法,
物件要提供 def __getitem__(self,key)
按照文件及我的需求(也就是要像一個 dict),
該物件要提供以下的 method,我完整的列出來
a = orderdict()

def has_key(self,key): 仿 dict 的 has_key
__setitem__(self,key,value): 提供 a[‘1a’] = “a”
__getitem__(self,key) 提供 b = a[‘1a’]
def __len__(self): 提供 len(a)
def __iter__(self): 提供 for i in a:
def __contains__(self,item): 依照文件,提供這個method,可以提高 in 這個 operation 的效率。參見3.4.5
def __repr__(self): 自定被 print 呼叫時的文字表達

完整的物件長像:

class orderdict():
    def __init__(self):
        self.list = []
        self.dict = {}
    def has_key(self,key):
        return self.dict.has_key(key)
    def __setitem__(self,key,value):
        if self.dict.has_key(key):
            self.dict[key] = value
        else:
            self.list.append(key)
            self.dict[key] = value
    def __getitem__(self,key):
        if self.has_key(key):
            return self.dict[key]
        else:
            raise KeyError
    def __delitem__(self, key):
        if self.has_key(key):
            del self.dict[key]
            self.list.remove(key)
    def __len__(self):
        return len(self.dict)
    def __iter__(self):
        return self.list.__iter__()
    def __contains__(self,item):
        return self.dict.has_key(item)
    def __repr__(self):
        return str(a.dict)

這樣子就有看來不錯的物件可以用了。
以後要寫物件自己用,一定要參考 The Python Language Reference >> 3. Data model
才會有好用的物件。

2011年8月26日 星期五

[編譯器的研究]parse tree之後?

是的,如同標題,在 parse tree 完成之後,應該做些什麼?
原本我是有目的的,要幫 log 上顏色。
在寫完以下的程式之後,我感覺到一陣的空虛。

f = open("genOutput.htm","w")
f.write("<html>")
f.write("<body>")
f.write("<table border='1'>")
for i in ms.get_attr("msg"):
  f.write("<tr><td>"+str(i)+"</td></tr>")
f.write("</table>")
f.write("</body>")
f.write("</html>")
f.close()

這段程式會輸出一個 html,瀏覽器會看到

image

要更好看,當然要繼續下去改善,像是顏色,欄位。或是加上搜尋功能,過濾功能。
但是在繼續下去之前,我想到一件事。
在使用上,是不是要即時一點?
例如,有新的事件進來,要即時 scan/parse,而現在,scanner/parser 是整個檔案 parse。
而我認為最好是採遞增式的 scanner/parser,就減少卡畫面的機會,一方面使用者會有比較好的使用者經驗;另一方面,處理時也不會突然讓 cpu usage 大爆增。
所以我想在這裡停下來,不往下繼續走。而是回頭再修改之前的 scanner/parser。

當然,如果有人要從這裡開始往下走,(我想大概也沒人看我這系列吧),
應該就開始偏離編譯器的範圍,也就不太會用這個系列開頭了。

這個系列算是結束了嗎?
不!
而是要開始比較正式的朝向 scanner/parser 的方向,以解析程式語言為目標,
以產生可執行程式或輔助開發環境為目的。
log monitor 的部份,會另外再開一條路來做。
而且我想,會需要遞增式 scanner/parser 的經驗來幫助。
所以要先把遞增式 scanner/parser 做好才對。

下次再見!

2011年8月24日 星期三

2011年8月15日 星期一

[VB6toVB.NET2010]

公司要把 VB6 的東西轉成 .NET 2010
上網找了一下,答案是不行直接轉。
http://www.google.com.tw/url?sa=t&source=web&cd=1&ved=0CBkQFjAA&url=http%3A%2F%2Fsocial.msdn.microsoft.com%2FForums%2Fzh-CN%2F2212%2Fthread%2Fd444bbea-d23d-48d2-b005-49d057405523%2F&ei=QeRJTtmaE4jniALamcGiBw&usg=AFQjCNFp3e09h6ajRzYMXJ7GCMGyEHRjwQ&sig2=nnlC759IJECozFq013x0aA

這一篇講得深得我心,尤其是最後一PO,真是爽啊。

所以該怎辦呢?
VS2003可以把VB6轉成.NET 的 project。
然後再用 VS2010 開啟轉換完的 project,再轉一次。
然後,form.vb 的部份只能看見UI程式碼,看不見UI外觀,哈哈!
執行一下失敗,需要許多舊時代的dll。
結論是還是要重寫一次。

恭喜!

[網路]Bounce Rate是什麼意思?

我有申請google的 analytics。
我把這個網站還有我用GAE做的一個網頁都加了進去。
我老是對於Bounce Rate很高不是很滿意。
但是,我內心是覺得我本來就不該在乎bounce rate。

bounce rate的嚴格描述在wiki上有說明http://en.wikipedia.org/wiki/Bounce_rate
我就不多說了。
依照定義,一般認為bounce rate越高越代表網頁的內容不好。
但是!
依照定義,部落格的bounce rate容易高,
假設,有人的部落格的讀者都是每天來看的人,今天的文章看完就走掉了,
bounce rate超高。
假設,我的網頁是提供當日訊息,只有一頁(我的另一個網頁就是如此),
根本沒其他頁,bounce rate沒有100%我還覺得奇怪。

好吧,我只是幫我的網頁沒什麼內容找藉口。

2011年8月4日 星期四

[web_service]自己寫一個 webservice server 及 client

因為要開發測試 web service 的應用程式,
最痛苦的地方就是 client 要連到已存在的 server,
而該 server 不能亂搞,而又不是自己寫的,沒有模擬器。
在一種最尷尬的情況就是,
你的程式就是 web service,還需要使用別人的 web service,
在沒有模擬器的狀況下,開發及除錯都很難,
一遇到要 call out 就跳過,是最保險,但又是最麻煩的做法。
有辦法自己寫一個 web service 的 client 及 server 嗎?
當然有,這裡介紹兩種方法:

第一種是用微軟的 Web Service 模組,這種是最偷懶的方法。
但這種方法一不小心就是會找不到問題在哪,
而我就是因為粗心而花了兩天找問題。
第二種是用任何語言的 http listener / client 模組,
這種方法是最直覺,最能夠把問題分離化的方法。
可以控制到 http 層當然控制度最好。
這一篇是因為開發測試的時候因為粗心大意,結果浪費時間的心得報告。

-----------------------------------------

使用微軟的Web Service 模組當「模擬 server」的時候,
可以只模擬部份的 web method,
如果 client 端有下列方法,

string LoadProduct( string xml)

「模擬 server」就要宣告一個方法如下:

[Web Method]
string LoadProduct( string xml) { ….. }

要非常注意兩件事,

一、WebServcie 的 namespace,要跟 client 端的 namespace「一模一樣」,
少一個,多一個字元都會導致解不開 soap 物件,而且不會有任何錯誤訊息出現。
我這次就是最尾端少一個斜線,http://tempurl.orghttp://tempurl.org/ 的差別。
找了兩天,做了很多測試,甚至自己用 http listener 來回應測試。

二、web method 的參數名字,也要跟 client 端的參數名字「一模一樣」,
名字不一樣,參數值一樣解不開。

我出錯在這兩個地方,只要這兩個地方沒有錯,雖然不保證一定成功,
但至少錯不在這!

使用微軟的Web Service 模組當「模擬 client」的時候,非常簡單,
這時候一定會有 server 來的 wsdl,就當正式的 client 來寫程式就行了。

-------------------------------------------------

用 http listener 當「模擬 server」的時候,要處理到 http 的回應,
若是不知道 soap 規格,也不知道 wsdl 怎麼看,又該如何處置呢?

抄!這是這次解決方法的最高秘技。
在微軟的2008開發環境中,把 web service 啟動之後,
可以連到類似 http://localhost:1234/abcd.asmx
點擊任何一個 web method 就會看見測試框。

image

也有範例,這就是我們要抄的地方。

image

如果要當 client,就抄第一段。如果是要當 server,就抄第二段。
如果你要模擬的 server 是用微軟工具開發的,網址尾端是 asmx 的,
就會有這個網頁,就有範例可以抄。

用 http listener 當「模擬 server」,以 .Net 為例,
System.Net.HttpListener 可以拿到對方送來的 context,
request 裡有對方送來的資料,需要的話在 request 裡找。
response 的話,從範例來看,要設定

response.ContentType = "text/xml; charset=utf-8";

把範例抄進來,弄一個變數 responseString 來存放。
為了要把它弄成 utf8,需要

byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);

response 要設定長度

response.ContentLength64 = buffer.Length;

最後把 byte 寫下去,還要記得關起來,就行了。

System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();

這就完成一次傳輸。
最簡單可重覆就是用一個無限迴圈,不要用的時候直接把程式關了。
有問題就關掉重開。這是最簡單的方式。

---------------------------------------------------------------

用 http client 當「模擬 client」,以 .Net 為例,
System.Net.WebClient 物件產生之後,
參考範例,要在 headers 設定 Content-Type,及 SOAPAction。
內容的部份用一個 string 變數 soapcontent,存起來。
然後用 UploadString 把 soapcontent 送出去,該 method 回傳 server 的回應。

比較完整的程式碼如下,有些可能在貼上來的時候,雙引號被弄掉了,
我有看到的我都補上了,剪下貼上的時候要看一下。
完整的 server 的 main 如下:

static void Main(string[] args)
        {
            System.Net.HttpListener hl = new System.Net.HttpListener();
            hl.Prefixes.Add("http://*:1234/");
            hl.Start();
            while (true)
            {
                System.Net.HttpListenerContext context = hl.GetContext();
                System.Net.HttpListenerRequest request = context.Request;
                System.IO.StreamReader sr = new System.IO.StreamReader(request.InputStream);
                Console.WriteLine("read:");
                Console.WriteLine(sr.ReadToEnd());
                System.Net.HttpListenerResponse response = context.Response;
                response.ContentType = "text/xml; charset=utf-8";
                string responseString = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><LoadProductResponse xmlns=\”http://tempurl.org/\”><LoadProductResult>string</LoadProductResult></LoadProductResponse></soap:Body></soap:Envelope>";
                Console.WriteLine("write:");
                Console.WriteLine(responseString);

                byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
                response.ContentLength64 = buffer.Length;
                System.IO.Stream output = response.OutputStream;
                output.Write(buffer, 0, buffer.Length);
                // You must close the output stream.
                output.Close();
            }
            hl.Stop();
        }

完整的 client 的 main 程式碼如下:

static void Main(string[] args)
{
                System.Net.WebClient wc = new System.Net.WebClient();
                string soapcontent = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">  <soap:Body>    <LoadProduct xmlns=\"http://mirlmes.itri.org.tw\">      <InXml>string</InXml>    </LoadProduct>  </soap:Body></soap:Envelope>";
                wc.Headers["SOAPAction"] = "\"http://tempurl.org/LoadProduct\"";
                wc.Headers["Content-Type"] = "text/xml; charset=utf-8";
                Console.WriteLine(wc.UploadString(“http://localhost:1234/”, soapcontent));
}

2011年8月1日 星期一

[編譯器的研究]parser的實作(2)

一般 parse tree 是一個有序(ordered)、有唯一根(rooted)的樹。
因此,之前的 grammar 要再改變一下,加上<log> ::= <msg> {<msg>} 在最前面。

<log> ::= <msg> {<msg>}
<msg> ::= timestamp <secs_type_msg>
          | timestamp <host_type_msg>
<secs_type_msg> ::= <desc> secs_comment <secs_msg>
<host_type_msg> ::= eap_log_1 {xml_transaction}
<desc> ::= <secs_msg_name> colon <secs_SxFy_sym>
          | normal_word {normal_word}
<secs_SxFy_sym> ::= secs_SxFy {<secs_w_bit>}
<secs_msg_name> ::= normal_word
<secs_w_bit> ::= normal_word
<secs_msg> ::= {<secs_msg_body>} secs_end
<secs_msg_body> ::= <secs_msg_item> {secs_comment }
<secs_msg_item> ::= secs_item_a
                    | secs_item_u
                    | secs_item_b
                    | secs_item_bool
                    | secs_item_f
                    | secs_item_i
                    | secs_list_start {secs_comment} {<secs_msg_item>} secs_list_end

接下來是設計 tree node 存放物件。

之前的程式實際上只有判斷 input 是否為可接受。
所以每一個函式傳回 true 的地方都改為傳回 node ,傳回 false 的地方則改為傳回 false。

最基本的一個 node,就是只有一個 name 屬性的物件。
把它當做抽象類別。為了方便序列化,我們把其他的屬性都用一個 dict 裝起來,
為了讓屬性按加入順序序列化,把屬性名字加到一個 list 中。

class c_node:
  def __init__(self):
    self.keyvalue = {}
    self.keyarray = []
  def add_attr(self,k,v):
    if self.keyvalue.has_key(k):
      self.keyvalue[k] = v
    else:
      self.keyarray.append(k)
      self.keyvalue[k] = v
  def set_attr(self,k,v):
    if self.keyvalue.has_key(k):
      self.keyvalue[k] = v
    else:
      self.keyarray.append(k)
      self.keyvalue[k] = v

  def get_attr(self,k):
    if self.keyvalue.has_key(k):
      return self.keyvalue[k]
    else:
      return None
  def __str__(self):
    _str = "["
    _str1 = []
    for i in self.keyarray:
      if self.keyvalue[i] != None:
        if type(self.keyvalue[i]) != list:
          _str1.append(i + ":" + str(self.keyvalue[i]))
        else:
          _str2 = []
          for j in self.keyvalue[i]:
            _str2.append( str(j) )
          _str1.append(i + ":" + "[" + ",".join(_str2) + "]")
      else:
        _str1.append(i + ":" + "None")
    _str += ",".join(_str1)
    _str += "]"
    return _str

我們為 log 設計一個 c_log 的類別。
class c_log(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','msgs')
    self.add_attr('msg',None)

其他的類別也很單純,只要按 grammar 中的順序把 symbol (terminal 及 non-terminal) 當做屬性加入即可。
但是到了 c_secs_msg_item,就不太一樣,因為他有可能是 terminal 也可能是 non-terminal 的 symbol,
所以屬性除了當做 terminal 存放值的 value,也有當做 non-terminal 存放值的 children。

class c_secs_msg_item(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','secs_msg_item')
    self.add_attr('list_start',None)
    self.add_attr('secs_comment',None)
    self.add_attr('children',None)
    self.add_attr('value',None)
    self.add_attr('list_end',None)

在 parser 的程式裡,遇到 secs_msg_item 的時候,
如果不是遇到 secs_list_start,就把值放到 value 去。
否則就把值放到 children 裡面,就算該值是空的。(因為允許空的 list)

(secs_msg_item 的程式碼請參考底下)

老實說,我也沒辦法一開始就知道需要 c_node,也沒辦法知道 c_node 就要長這個樣子。
我是邊寫邊改的。因為要 debug 才生出 __str__ 這個方法。
因為要 __str__ 才知道要把屬性放到 list 及 dict 裡面方便列舉。
在寫的過程中,參考了 google code 裡面的 traceur。(它是用 javascript 寫的)
因為難以了解之後會遇到什麼,所以花了一點時間研究才隔了兩星期沒有產生(有一點找藉口)
完整的程式碼我分成兩個,一個是 node 們的定義。一個是 parser 的程式。

node_def.py

class c_node:
  def __init__(self):
    self.keyvalue = {}
    self.keyarray = []
  def add_attr(self,k,v):
    if self.keyvalue.has_key(k):
      self.keyvalue[k] = v
    else:
      self.keyarray.append(k)
      self.keyvalue[k] = v
  def set_attr(self,k,v):
    if self.keyvalue.has_key(k):
      self.keyvalue[k] = v
    else:
      self.keyarray.append(k)
      self.keyvalue[k] = v

  def get_attr(self,k):
    if self.keyvalue.has_key(k):
      return self.keyvalue[k]
    else:
      return None
  def __str__(self):
    _str = "["
    _str1 = []
    for i in self.keyarray:
      if self.keyvalue[i] != None:
        if type(self.keyvalue[i]) != list:
          _str1.append(i + ":" + str(self.keyvalue[i]))
        else:
          _str2 = []
          for j in self.keyvalue[i]:
            _str2.append( str(j) )
          _str1.append(i + ":" + "[" + ",".join(_str2) + "]")
      else:
        _str1.append(i + ":" + "None")
    _str += ",".join(_str1)
    _str += "]"
    return _str

class c_log(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','msgs')
    self.add_attr('msg',None)

class c_msg(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','msg')
    self.add_attr('timestamp',None)
    self.add_attr('host_type_msg',None)
    self.add_attr('secs_type_msg',None)

class c_secs_type_msg(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','secs_type_msg')
    self.add_attr('desc',None)
    self.add_attr('secs_comment',None)
    self.add_attr('secs_msg',None)
class c_secs_SxFy_sym(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','secs_SxFy_sym')
    self.add_attr('secs_SxFy',None)
    self.add_attr('secs_w_bit',None)

class c_desc(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.name = 'desc'
    self.add_attr('name','desc')
    self.add_attr('secs_msg_name',None)
    self.add_attr('colon',None)
    self.add_attr('secs_SxFy_sym',None)
    self.add_attr('normal_word',None)

class c_secs_msg_name(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','secs_msg_name')
    self.add_attr('normal_word',None)

class c_colon(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','colon')
    self.add_attr('value',':')

class c_secs_w_bit(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.name = 'secs_w_bit'
    self.add_attr('name',self.name)
    self.normal_word = None   
    self.add_attr('normal_word',self.normal_word)

class c_secs_msg(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','secs_msg')
    self.add_attr('secs_msg_body',None)
    self.add_attr('secs_end',None)

class c_secs_msg_body(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','secs_msg_body')
    self.add_attr('secs_msg_item',None)
    self.add_attr('secs_comment',None)
class c_secs_msg_item(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','secs_msg_item')
    self.add_attr('list_start',None)
    self.add_attr('secs_comment',None)
    self.add_attr('children',None)
    self.add_attr('value',None)
    self.add_attr('list_end',None)

class c_host_type_msg(c_node):
  def __init__(self):
    c_node.__init__(self)
    self.add_attr('name','host_type_msg')
    self.add_attr('eap_log_1',None)
    self.add_attr('xml_transaction',None)

------------------------------------------------------------------------------
ll_parser_v4.py  # 看到這個 v4 就知道我大改了4次。

import node_def

t_file = open("tokens.txt","r")
t_file_content = t_file.read()
t_file.close()

tokens = t_file_content.split("\n\n\n")
global tokenindex
tokenindex = 0

global_stack = []

def token_cut(token):
  r = []
  sep = ":"
  t1 = token[:token.find(sep)]
  token = token[token.find(sep)+1:]
  sep = ","
  t1 = token[:token.find(sep)]
  token = token[token.find(sep)+1:]
  r.append(t1)
  sep = ","
  t1 = token[:token.find(sep)]
  token = token[token.find(sep)+1:]
  r.append(t1)
  sep = ","
  t1 = token[:token.find(sep)]
  token = token[token.find(sep)+1:]
  r.append(t1)

  sep = ","
  t1 = token[:token.find(sep)]
  token = token[token.find(sep)+1:]
  r.append(t1)
  sep = ","
  t1 = token[:token.find(sep)]
  token = token[token.find(sep)+1:]
  r.append(t1)
  r.append(token)
  return r

input_index = -1
sym = None
tokencount = len(tokens)
def getsym():
  global input_index
  input_index += 1
  global sym
  if input_index > tokencount - 1:
    sym = None
  else:
    if len(tokens[input_index])==0:
      sym = None
    else:
      sym = token_cut(tokens[input_index])

def lookahead():
  global input_index
  if input_index + 1 > tokencount - 1:
    return None
  else:
    return token_cut(tokens[input_index + 1])
def accept(s):
  global sym
  global global_stack
  if sym != None and sym[0] == s:
    #print 'accept',sym
    global_stack.append(sym[5])
    getsym()
    return True
  return False

def expect(s):
  if accept(s):
    return True
  print "expect: unexpected symbol"
  print "expect:", s
  global sym
  print "encounter:",sym
  sym = None
  return False

def secs_w_bit():
  print 'secs_w_bit()'
  my = node_def.c_secs_w_bit()
  r = accept("normal_word")
  if r:
    v = global_stack.pop()
    my.set_attr('normal_word',v)
    #print 'normal_word pop:',v
  #print str(my)
  print 'secs_w_bit() complete'
  return my

def secs_SxFy_sym():
  print 'secs_SxFy_sym()'
  my = node_def.c_secs_SxFy_sym()
  r = expect("secs_SxFy")
  if r:
    v = global_stack.pop()
    my.set_attr("secs_SxFy",v)
    #print 'secs_SxFy pop:',v
  my.set_attr("secs_w_bit",secs_w_bit())
  #print str(my)
  print 'secs_SxFy_sym() complete'
  return my

def secs_msg_name():
  print 'secs_msg_name()'
  my = node_def.c_secs_msg_name()
  r = expect("normal_word")
  if r:
    v = global_stack.pop()
    my.normal_word = v
    #print 'normal_word pop:',v
  #print str(my)
  print 'secs_msg_name() complete'
  return my

def desc():
  print 'desc()'
  my =  node_def.c_desc()
  global input_index
  _sym = lookahead()
  if _sym[0]=="colon":
    my.set_attr("secs_msg_name",secs_msg_name())
    r = expect("colon")
    if r:
      v = global_stack.pop()
      my.set_attr("colon",v)
      #print 'colon pop:', v
    my.set_attr("secs_SxFy_sym",secs_SxFy_sym())
  else:
    r = accept("normal_word")
    if r:
      v = global_stack.pop()
      my.set_attr("normal_word",v)
      #print 'normal_word pop:',v
    while r == True:
      r = accept("normal_word")
      if r:
        v = global_stack.pop()
        if type(my.get_attr("normal_word")) == list:
          my.get_attr("normal_word").append(v)
        else:
          tmpv = my.get_attr("normal_word")
          my.set_attr("normal_word",[])
          my.get_attr("normal_word").append(tmpv)
          my.get_attr("normal_word").append(v)
        #print 'normal_word pop:',v
  #print str(my)
  print 'desc() complete'
  return my

def secs_msg_item():
  print 'secs_msg_item()'
  my = node_def.c_secs_msg_item()
  if accept("secs_item_a"):
    v = global_stack.pop()
    my.set_attr("value", v)
    r = accept("secs_comment")
    if r:
      v = global_stack.pop()
      my.set_attr("secs_comment", v)
  elif accept("secs_item_u"):
    v = global_stack.pop()
    my.set_attr("value", v)
    r = accept("secs_comment")
    if r:
      v = global_stack.pop()
      my.set_attr("secs_comment", v)
  elif accept("secs_item_b"):
    v = global_stack.pop()
    my.set_attr("value", v)
    r = accept("secs_comment")
    if r:
      v = global_stack.pop()
      my.set_attr("secs_comment", v)
  elif accept("secs_item_bool"):
    v = global_stack.pop()
    my.set_attr("value", v)
    r = accept("secs_comment")
    if r:
      v = global_stack.pop()
      my.set_attr("secs_comment", v)
  elif accept("secs_item_f"):
    v = global_stack.pop()
    my.set_attr("value", v)
    r = accept("secs_comment")
    if r:
      v = global_stack.pop()
      my.set_attr("secs_comment", v)
  elif accept("secs_item_i"):
    v = global_stack.pop()
    my.set_attr("value", v)
    r = accept("secs_comment")
    if r:
      v = global_stack.pop()
      my.set_attr("secs_comment", v)
  elif accept("secs_list_start"):
    v = global_stack.pop()
    my.set_attr("list_start", v)
    r = accept("secs_comment")
    if r:
      v = global_stack.pop()
      my.set_attr("secs_comment", v)
    r = secs_msg_item()
    if r != None:
      my.set_attr("children", [])
    while r != None:
      my.get_attr("children").append(r)
      r = secs_msg_item()
    r = expect("secs_list_end")
    if r:
      v = global_stack.pop()
      my.set_attr("list_end", v)
  else:
    print 'secs_msg_item() complete'
    return None
  #print str(my)
  print 'secs_msg_item() complete'
  return my

def secs_msg_body():
  print 'secs_msg_body()'
  my = node_def.c_secs_msg_body()
  my.secs_msg_item = secs_msg_item()
  r = accept("secs_comment")
  if r:
    v = global_stack.pop()
    self.secs_comment = v
    #print 'secs_comment pop:',global_stack.pop()
  #print str(my)
  print 'secs_msg_body() complete'
  return my

def secs_msg():
  print 'secs_msg()'
  my = node_def.c_secs_msg()
  if accept("secs_end"):
    v = global_stack.pop()
    my.set_attr("secs_end",v)
    #print 'secs_end pop:',v
  else:
    my.secs_msg_body = secs_msg_body()
    r = accept("secs_end")
    if r:
      v = global_stack.pop()
      my.set_attr("secs_end",v)
      #print 'secs_end pop:',v
  #print str(my)
  print 'secs_msg() complete'
  return my

def secs_type_msg():
  print 'secs_type_msg()'
  my = node_def.c_secs_type_msg()
  my.set_attr('desc',desc())
  r = accept("secs_comment")
  if r:
    v = global_stack.pop()
    my.set_attr('secs_comment',v)
    #print 'secs_comment pop:',v
  my.set_attr('secs_msg',secs_msg())
  #print str(my)
  print 'secs_type_msg() complete'
  return my

def host_type_msg():
  print 'host_type_msg()'
  my = node_def.c_host_type_msg()
  r = expect("eap_log_1")
  if r:
    v = global_stack.pop()
    my.set_attr('eap_log_1',v)
    r = accept("eap_log_1")
    while r:
      v = global_stack.pop()
      if type(my.get_attr('eap_log_1')) == list:
        my.get_attr('eap_log_1').append(v)
      else:
        tmpv = my.get_attr('eap_log_1')
        my.set_attr('eap_log_1',[])
        my.get_attr('eap_log_1').append(tmpv)
        my.get_attr('eap_log_1').append(v)
      r = accept("eap_log_1")
    #print 'eap_log_1 pop:',global_stack.pop()
  r = accept("xml_transaction")
  if r:
    v = global_stack.pop()
    my.set_attr('xml_transaction',v)
    #print 'xml_transaction pop:',global_stack.pop()
  print 'host_type_msg() complete'
  return my
def msg():
  print 'msg()'
  my = node_def.c_msg()
  if expect("timestamp"):
    v = global_stack.pop()
    my.add_attr('timestamp',v)
    #print 'timestamp pop:',v
    if sym[0]=="eap_log_1":
      my.add_attr('host_type_msg',host_type_msg())
    else:
      my.add_attr('secs_type_msg',secs_type_msg())
  print 'msg() complete'
  return my

ms = node_def.c_log()
getsym()
while sym != None:
  print 'token count',input_index
  a = msg()
##  print 'a.timestamp',a.timestamp
##  print 'a.host_type_msg',a.host_type_msg
##  print 'a.secs_type_msg',a.secs_type_msg
  if type(ms.get_attr("msg")) != list:
    ms.set_attr("msg",[])
  ms.get_attr("msg").append(a)

f = open("v4output.txt","w")
f.write(str(ms))
f.close()

-------------------------
tokens_for_dev.txt  #用來測試用的輸入檔

tokens(248743):timestamp,1,True,3302688,62420,2011-06-22-23:59:55

tokens(248745):normal_word,3,True,3302708,62420,AYT_Host

tokens(248746):colon,4,True,3302716,62420,:

tokens(248748):secs_SxFy,5,True,3302718,62420,'S1F1'

tokens(248750):normal_word,3,True,3302725,62420,W

tokens(248752):secs_comment,6,True,3302727,62420,/* Name=AreYouThere_Host Dir=2 Header=[00 00 81 01 00 00 00 01 CA 55] Rcvd=2 Time=23:59:55 TID=31094 */

tokens(248754):secs_end,8,True,3302831,62421,.

tokens(248756):timestamp,1,True,3302833,62422,2011-06-22-23:59:55

tokens(248758):normal_word,3,True,3302853,62422,OLD

tokens(248759):colon,4,True,3302856,62422,:

tokens(248761):secs_SxFy,5,True,3302858,62422,'S1F2'

tokens(248763):secs_comment,6,True,3302865,62422,/* Name=OnlineData Dir=1 Header=[00 00 01 02 00 00 00 01 CA 55] Rcvd=1 Time=23:59:55 TID=31094 */

tokens(248766):secs_list_start,9,True,3302967,62423,<L [2]

tokens(248769):secs_item_a,10,True,3302982,62424,<A [0] >

tokens(248771):secs_comment,6,True,3302991,62424,/* Name=MDLN Keyword=EquipmentModelType */

tokens(248774):secs_item_a,10,True,3303042,62425,<A [0] >

tokens(248776):secs_comment,6,True,3303051,62425,/* Name=SOFTREV Keyword=SoftwareRevision */

tokens(248779):secs_list_end,11,True,3303100,62426,>

tokens(248781):secs_end,8,True,3303102,62427,.

tokens(41):timestamp,1,True,416,8,2011-06-22-07:56:17

tokens(43):eap_log_1,12,True,436,8,Receiving PP_Upload request from TCS...

tokens(45):xml_transaction,13,True,476,9,<?xml version="1.0"?>
<Transaction TxName="PP_Upload" Type="Request" MessageKey="0629">
    <Tool ToolID="CLB02" fromOPI="true" Type=""/>
    <Recipes>
        <Recipe RecipeID="APM_6MIN" MachineRecipeID="CLB02.APM_6MIN.AA" LocalFilePath="D:\share\110622_075617_304_0082706463_00" FilePath="" FormatFlag="1" Type="" RecipeLevel="1" Option=""/>
        <Recipe RecipeID="Production;MXIC_SC1_360;1" MachineRecipeID="CLB02.APM_6MIN.AA" LocalFilePath="D:\share\110622_075617_304_0082706463_01" FilePath="" FormatFlag="1" Type="" RecipeLevel="2" Option=""/>
        <Recipe RecipeID="Production;POR QDR-HCL;1" MachineRecipeID="CLB02.APM_6MIN.AA" LocalFilePath="D:\share\110622_075617_304_0082706463_02" FilePath="" FormatFlag="1" Type="" RecipeLevel="2" Option=""/>
        <Recipe RecipeID="Production;POR-philic;1" MachineRecipeID="CLB02.APM_6MIN.AA" LocalFilePath="D:\share\110622_075617_304_0082706463_03" FilePath="" FormatFlag="1" Type="" RecipeLevel="2" Option=""/>
    </Recipes>
</Transaction>

tokens(47):timestamp,1,True,1442,20,2011-06-22-07:56:17

tokens(49):eap_log_1,12,True,1462,20,[S7F5] Sent by Host

--------------------------------------------------------------
v4output.txt # 最後的結果給大家參考,有點像是 json。也許有天我會改成 json。

[name:msgs,msg:[[name:msg,timestamp:2011-06-22-23:59:55,host_type_msg:None,secs_type_msg:[name:secs_type_msg,desc:[name:desc,secs_msg_name:[name:secs_msg_name,normal_word:None],colon::,secs_SxFy_sym:[name:secs_SxFy_sym,secs_SxFy:'S1F1',secs_w_bit:[name:secs_w_bit,normal_word:W]],normal_word:None],secs_comment:/* Name=AreYouThere_Host Dir=2 Header=[00 00 81 01 00 00 00 01 CA 55] Rcvd=2 Time=23:59:55 TID=31094 */,secs_msg:[name:secs_msg,secs_msg_body:None,secs_end:.]]],[name:msg,timestamp:2011-06-22-23:59:55,host_type_msg:None,secs_type_msg:[name:secs_type_msg,desc:[name:desc,secs_msg_name:[name:secs_msg_name,normal_word:None],colon::,secs_SxFy_sym:[name:secs_SxFy_sym,secs_SxFy:'S1F2',secs_w_bit:[name:secs_w_bit,normal_word:None]],normal_word:None],secs_comment:/* Name=OnlineData Dir=1 Header=[00 00 01 02 00 00 00 01 CA 55] Rcvd=1 Time=23:59:55 TID=31094 */,secs_msg:[name:secs_msg,secs_msg_body:None,secs_end:.]]],[name:msg,timestamp:2011-06-22-07:56:17,host_type_msg:[name:host_type_msg,eap_log_1:Receiving PP_Upload request from TCS...,xml_transaction:<?xml version="1.0"?>
<Transaction TxName="PP_Upload" Type="Request" MessageKey="0629">
    <Tool ToolID="CLB02" fromOPI="true" Type=""/>
    <Recipes>
        <Recipe RecipeID="APM_6MIN" MachineRecipeID="CLB02.APM_6MIN.AA" LocalFilePath="D:\share\110622_075617_304_0082706463_00" FilePath="" FormatFlag="1" Type="" RecipeLevel="1" Option=""/>
        <Recipe RecipeID="Production;MXIC_SC1_360;1" MachineRecipeID="CLB02.APM_6MIN.AA" LocalFilePath="D:\share\110622_075617_304_0082706463_01" FilePath="" FormatFlag="1" Type="" RecipeLevel="2" Option=""/>
        <Recipe RecipeID="Production;POR QDR-HCL;1" MachineRecipeID="CLB02.APM_6MIN.AA" LocalFilePath="D:\share\110622_075617_304_0082706463_02" FilePath="" FormatFlag="1" Type="" RecipeLevel="2" Option=""/>
        <Recipe RecipeID="Production;POR-philic;1" MachineRecipeID="CLB02.APM_6MIN.AA" LocalFilePath="D:\share\110622_075617_304_0082706463_03" FilePath="" FormatFlag="1" Type="" RecipeLevel="2" Option=""/>
    </Recipes>
</Transaction>],secs_type_msg:None],[name:msg,timestamp:2011-06-22-07:56:17,host_type_msg:[name:host_type_msg,eap_log_1:[S7F5] Sent by Host

,xml_transaction:None],secs_type_msg:None]]]

--------------------------------------------------------------
能看到這的人不是正常人,恭喜你。
正常的編譯器接下來進到 code generation。
而我們這次的題目,接下來呢?
應該要上色了。