小例子學習PCYACC和編譯器開發

2021-09-30 02:03:01 字數 4269 閱讀 7054

v. getting started -- a small example

v. 現在開始-乙個簡單例子

這一章的目的是教您怎樣在乙個語言開發專案中使用pcyacc,為了達到這個目標,我們假定您熟悉c語言。我們同樣假定您有乙份abraxas pcyacc和乙個c語言編譯器(c compiler)。

這一章給你使用pcyacc的程式開發流程的概述。本章的例子是乙個簡單的計算器,它能夠做普通算術操作。這個例子將會向您展示它-我們暫時定名為sacalc(****** arithmetic calculator program)的pcyacc程式清單,同時舉例說明怎樣用pcyacc和c編譯器建立可執行的sacalc,在稍後的一章中我們還會用乙個稍微高階一點的例子來詳細描述開發過程。

1.sacalc的語法描述檔案(grammar description file)

下面是這個簡單計算器例子的gdf**檔案:sacalc.y,為了引用的方便,我們在其中加入了行號(請注意,行號不能出現在您的gdl檔案中)

01: %

06:07: %token number

08: %left '+' '-' /* left associative */

09: %left '*' '/' /* left associative */

10: %left unaryminus

11:12: %%

13:14: list: /* nothing */

15: | list '/n'

16: | list expr '/n'

17:

18: | list error '/n'

19:

20: ;

21:22: expr: number

23:

24: | '-' expr %prec unaryminus

25:

26: | expr '+' expr

27:

28: | expr '-' expr

29:

30: | expr '*' expr

31:

32: | expr '/' expr

33:

34: | '(' expr ')'

35:

36: ;

37:38: %%

39:40: #include

41: #include

42: char *progname; /* for error messages */

43: int lineno = 1;

44:45: main(argc, ar**)

46: char *ar**;

47:

51:52: yylex()

53:

65: if (c == '/n')

66: lineno++;

67: return c;

68: }

69:70: yyerror(s) /* called on syntax error */

71: char *s;

72:

75:76: warning(s, t) /* print warning message */

77: char *s, *t;

78:

83:這個短小的例子,展示了乙個pcyacc語法描述程式(grammar description program)的典型結構和組成部分,line1到11稱作定義段(declaration section),符號、操作符優先順序等等就是定義在這裡。line12到37組成了文法規則段(grammar rule section),開發和放置這個語言的文法規則。line38到83是程式段(program section),這裡放置這個編譯器的c支援**。就像這個例子中看到的,乙個語法描述程式由三部分組成:定義段、文法規則段和程式段。其中的定義段、程式段可以為空(沒有定義段時,分隔符"%%"仍然需要,它告訴pcyacc開始處理文法規則段)。

成對出現在line1和line5的符號是一對分隔符,在定義段中,它們用來包含c的宣告,例如預處理指令、全域性資料結構定義或者變數宣告(在本例子中,就是乙個預處理指令。)pcyacc不會檢視在此分隔符裡面的**。在分隔符裡面的任何東西都將毫無變化的傳遞給c程式(即產生的編譯器**)

line7宣告了number為乙個記號(token),token是一種語法符號,它不能用在文法規則的左邊。(當yylex被呼叫的時候,我們假設yylex返回它發現的token的型別。number要被定義在產生**裡面的乙個"#define"語句中。如果yylex不是產生程式的一部分,它還可以被放進乙個標頭檔案中。當yylex被呼叫的時候,產生的分析器(the generated parser)期望收到token的型別,例如number和標誌檔案結尾的0。)

8到10行定義了和運算元(the arithmetic operators)有關的結合式(associativity)。所有的運算元被定義成左結合的(比如,在a+b+c這個式子中,a+b會被首先計算)。這些式子同樣傳達了以下資訊:加(+)和減(-)有同樣的優先順序;乘(*)和除(/)有同樣的優先順序並且高於加和減;負號,這個一元運算元,有最高的優先順序。

12行是乙個分隔符,它隔開了定義段和文法規則段,

14行到20行可以簡單記做以下的四個規則:

(1) list : ;

(2) list : list '/n' ;

(3) list : list expr '/n' ;

(4) list : list error '/n' ;

這些規則說:乙個list不僅可以是空的(1),而且可以是乙個list後面跟著乙個換行符號('/n')(2),還可以是乙個list後面跟著乙個表示式(expr)又乙個換行符(3),又或者是乙個list後面跟著什麼錯誤之類的(error)(4)。在簡單符號中,冒號(:)是用來分隔開文法規則的左部的。豎線(|)是用來分隔開乙個普通的左邊非終結符,或者非終結符的可選項。分號(;)則用來結束這條文法規則。在list文法規則中,17行和19行,都是附加的指令,叫做行為(actions)。類似的,22行到36行定義了expressions的文法規則。

第二個"%%"分隔符隔開了文法規則段(grammar rule section)和程式段(program section).在程式斷中的所有東西也被拷貝到pcyacc的輸出檔案中。這個段定義了三個c函式:main(),yylex(),yyerror().記住在pcyacc生成的支援產生式分析器中,這三個c函式總是需要的。(它們可以在不同的檔案中,只是簡單地在程式發生時鏈結)。下面地討論會幫助你明白:怎樣將pcyacc的產生**和程式設計師用編寫的支援函式結合在一起,組成乙個完整的c程式。

pcyacc的產生**是乙個c函式:yyparse(),在技術上,它是乙個gdf檔案中定義的語言的文法規則(grammar rule)的lalr分析器(lalr parser),使用者預設的main函式:main(),是用來啟用分析器的,啟用的執行前需要初始化,在啟用後需要釋放。詞法分析器:yylex(),是在分析器前後出現的(is the front end of the parser).詞法分析器(the lexical analyzer)被認為是分解自然文字字串為有意義的詞彙單元的,這個詞彙單元就叫做tokens,同時把這個資訊傳遞給parser.錯誤處理過程:yyerror(),當parser解析的過程中乙個句法錯誤(syntax error)未被發現時呼叫此函式。這四個函式的關係在下圖中可以看到:

這三個段:定義段、文法規則段、程式段,還會在後面的章節中更多的討論到。

2.建立能夠執行的sacalc

呼叫pcyacc去解析語法描述檔案sacalc.y,使用以下的命令:

pcyacc sacalc.y

這個操作的結果是乙個c程式檔案sacalc.c,它被建立在當前目錄下,這個是計算器的c**,要獲得sacalc.c的執行版本,呼叫c的編譯器如下所示(假定這裡是microsoft c compiler)

cl sacalc.c

此操作將產生目標檔案sacalc.obj,現在使用link命令產生真正的msdos工具:

link sacalc.obj

(注意:這裡的"make"檔案,sacalc.mak,可以在msdos下被呼叫,以自動生成執行碼。如果你使用不同的c compiler,你需要遵守你的編譯器的使用方法去建立乙個應用程式,這裡的應用程式有很多含義,可能是乙個對話方塊,接受輸入同時顯示結果)。

3.sacalc使用的例子

當sacalc的可執行**成功建立後(在我們的例子中,我們就有了乙個sacalc在當前目錄下),我們可以使用它去執行簡單的算術運算。下面是sacalc工作的例子:

sacalc

1 + 2

2.5 + 4 * 1.5

8.52 / 4 - 3 * 0.5 / 3 + 5

5

GCC編譯器學習

不同的平台如x86和arm,一段程式跑起來到最下面會轉成彙編,彙編要轉成機器碼,機器碼會由於硬體平台不同而不同。有時候要程式設計序在arm上跑,必須針對arm寫程式,但是在arm上寫程式很麻煩 沒有很好的開發工具 所以我們就在x86平台上 windows 上把程式寫好,但是編譯的時候告訴它我們要執行...

C 學習(9) 配置編譯器 編譯器擴充套件

亞歷克斯於2018年9月19日 最後由alex於2018年9月26日修改 c 標準定義了程式在特定情況下應如何表現的規則。在大多數情況下,編譯器將遵循這些規則。但是,許多編譯器實現了對語言的更改,通常是為了增強與其他語言版本 例如c99 的相容性,或者出於歷史原因。這些特定於編譯器的行為稱為編譯器擴...

學習筆記 vi 編譯器

linux 使用文字檔案來保持配置檔案 文字編輯器 ascii檔案 emacs vi vi visual inte ce 全屏文字編輯,nano 模式化的編輯器 moduler vim vi improved vi的模式 輸入模式 末行模式 vim的內建的命令列介面,執行vim內建命令 編輯模式 輸...