[Reprint] SDL usage, Part 1: Lex and YACC-Build a syntax analyzer for script and GUI Design

Source: Internet
Author: User
SDL usage, Part 1: Lex and YACC
Build a syntax analyzer for script and GUI Design

Sam lantinga and Lauren mconell
Loki Entertainment Software
Skillednursing.com
May 2000

Content:
Another section: bison
Starting from basics
Provide input
Use Lex and YACC with C ++
Analyze strings
Use multiple syntax analyzers
Script Language
Gui example
Conclusion
References
About the author

In this section, we will discuss two utility tools in all Linux programmer tool libraries: Lex and YACC. These tools allow us to easily build our Linux game Pirates ho in our SDL-based Linux game! The script language and GUI framework used in.

In designing Pirates ho! We need a simple method to describe the interface and dialog box options to players. We need simple, consistent, and flexible languages to describe, so we look for tools that can help us build scripting languages.

Who wants another bison?
When I was at school, I was full of fear for the word "YACC. It reminds me of students with messy hair and pale faces whispering compiler and symbol table. So I am very careful to avoid using the Compiler class. But when I was developing a game, I had the courage to use YACC, hoping it would make it easier to write scripts. Finally, YACC not only makes writing scripts easier, but also makes the process interesting.

Starting from basics
YACC is actually very easy to use. As long as it is provided with a set of rules that describe the syntax, it can analyze the tag and take the action according to the seen. For the scripting language we use, we want to go from simple to deep. At first, we only specify some numbers and their logical operations:

Eval. Y


%{/* This first section contains C code which will be included in the output   file.*/#include <stdlib.h>#include <stdio.h>/* Since we are using C++, we need to specify the prototypes for some    internal yacc functions so that they can be found at link time.*/extern int yylex(void);extern void yyerror(char *msg);%}/* This is a union of the different types of values that a token can   take on.  In our case we'll just handle "numbers", which are of   C int type.*/%union {int number;}/* These are untyped tokens which are recognized as part of the grammar */%token AND OR EQUALS/* Here we are, any NUMBER token is stored in the number member of the   union above.*/%token  NUMBER/* These rules all return a numeric value */%type  expression%type  logical_expression and or equals%%/* Our language consists either of a single statement or of a list of statements.   Notice the recursivity of the rule, this allows us to have any   number of statements in a statement list.*/statement_list: statement | statement_list statement;/* A statement is simply an expression.  When the parser sees an expression   we print out its value for debugging purposes.  Later on we'll   have more than just expressions in our statements.*/statement: expression{ printf("Expression = %d/n", $1); };/* An expression can be a number or a logical expression. */expression: NUMBER|   logical_expression;/* We have a few different types of logical expressions */logical_expression: and|           or|           equals;/* When the parser sees two expressions surrounded by parenthesis and   connected by the AND token, it will actually perform a C logical   expression and store the result into   this statement.*/and: '(' expression AND expression ')'{ if ( $2 && $4 ) { $$ = 1; } else { $$ = 0; } };or: '(' expression OR expression ')'{ if ( $2 || $4 ) { $$ = 1; } else { $$ = 0; } };equals: '(' expression EQUALS expression ')'{ if ( $2 == $4 ) { $$ = 1; } else { $$ = 0; } };%%/* This is a sample main() function that just parses standard input   using our yacc grammar.  It allows us to feed sample scripts in   and see if they are parsed correctly.*/int main(int argc, char *argv[]){yyparse();}/* This is an error function used by yacc, and must be defined */-void yyerror(char *message){fprintf(stderr, "%s/n", message);}

How to provide input?
Now that we have a simple syntax that can identify tag sequences, we will need to look for a way to provide these tags to the syntax analyzer. Lex is a tool that accepts input, converts it to tags, and passes these tags to YACC. Next, we will describe Lex to convert it into a markup expression:

Eval. L


%{/* Again, this is C code that is inserted into the beginning of the output */#include #include "y.tab.h"  /* Include the token definitions generated by yacc */%}/* Prevent the need for linking with -lfl */%option noyywrap/* This next section is a set of regular expressions that describe input   tokens that are passed back to yacc.  The tokens are defined in y.tab.h,   which is generated by yacc. */%%////.*/* ignore comments */-[0-9]+|[0-9]+{ yylval.number=atoi(yytext); return NUMBER; }[ /t/n]/* ignore whitespace */&&{ return AND; }/|/|{ return OR; }=={ return EQUALS; }.return yytext[0];%%

Now, the analysis source code is available in the current directory. We need a makefile to build them:

Makefile

all: eval y.tab.c: eval.yyacc -d $<lex.yy.c: eval.llex $<eval: y.tab.o lex.yy.o$(CC) -o $@ $^

By default, YACC outputs to Y. Tab. C, and Lex outputs to Lex. yy. C, so we use those names as source files. Makefile contains rules for building source code based on the analysis description file. After everything is ready, you can enter "make" to build the syntax analyzer. Then we can run the syntax analyzer and input the script to check the logic.

Transfer/compression conflict
A transfer/compression conflict occurs when YACC must select between analyzing a group of tags or parsing them into rules. For example, if you create a syntax consisting of the following:
expression: NUMBER | plus;plus: expression '+' expression;

When the YACC syntax analyzer sees a number, it does not know whether to parse the number into an expression immediately or wait for "x + y ". In this case, YACC will warn of transfer/compression conflicts. By default, it will wait for the complete "X + Y" expression. If '-V' is specified for GNU bison, A * is created *. output file, which contains the rules and conflicts that have occurred. You can use this file to know What happened when GNU bison was used.

You only need to add parentheses on both sides of the plus expression to solve this ambiguity problem easily:

expression: NUMBER | plus;plus: '(' expression '+' expression ')';

Use Lex and YACC with C ++
I have some advice on using lex and YACC with C ++. Lex and YACC are output to C files. Therefore, for C ++, we use GNU equivalents flex and bison. These tools allow you to specify the name of the output file. We also add general rules to makefile, so GNU make will automatically build C ++ source files based on Lex and YACC source code. This requires us to rename the lex and YACC source code to "lex_eval.l" and "yacc_eval.y" respectively, so that make will generate different C ++ source files for them. You also need to change the files that lex uses to store the YACC tag definition. The header file output by bison uses an output file name with the. h suffix. In this example, it is "yacc_eval.cpp.h ". The new makefile is as follows:

Makefile


all: eval %.cpp: %.ybison -d -o $@ $<%.cpp: %.lflex -o$@ $<yacc_eval.o: yacc_eval.cpplex_eval.o: lex_eval.cppeval: yacc_eval.o lex_eval.o$(CXX) -o $@ $^

Analyze strings
The default Lex Code reads its input from the standard input, but we want the game to be able to analyze strings in memory. It is easy to use flex, as long as the macro at the top of the lex source file is redefinedYY_INPUT:


extern int eval_getinput(char *buf, int maxlen);#undef YY_INPUT#define YY_INPUT(buf, retval, maxlen)(retval = eval_getinput(buf, maxlen))

We write the actual code of eval_getinput () into a separate file to make it very flexible, so that it can get the input from the file pointer or a string in the memory. To use the actual code, we first create a global data source variable and then call the YACC function yyparse (). This function calls the input function and analyzes it.

Use multiple syntax analyzers
We want to use different syntax analyzers for the scripting language and GUI description in the game because they use different syntax rules. This is feasible, but we must use some tips for flex and bison. First, you need to change the prefix of the syntax analyzer from "YY" to a unique name to avoid name conflict. You only need to use the command line option for flex and bison to rename the syntax analyzer.-PFor bison-p. Then, you must replace the prefix "YY" in the Code with the selected prefix. This includes the Lex source code.yylvalAndyyerror()Because we put it in a separate file of the last game. The final makefile is as follows:

Makefile


all: eval YY_PREFIX = eval_%.cpp: %.ybison -p$(YY_PREFIX) -d -o $@ $<%.cpp: %.lflex -P$(YY_PREFIX) -o$@ $<yacc_eval.o: yacc_eval.cpplex_eval.o: lex_eval.cppeval: yacc_eval.o lex_eval.o$(CXX) -o $@ $^

Script Language
Starting with the code shown above (you can find the download URL in the reference materials), we will continue to add support for functions, variables, and simple traffic control, finally, we get a fairly complete explanatory language for the game. The following is a possible script sample:

Example.txt


function whitewash{        if ( $1 == "Blackbeard" ) {                print("Pouring whitewash on Blackbeard!")                if ( $rum >= 3 ) {                        print("Pouring whitewash on Blackbeard!")                        mood = "happy"                } else {                        print($1, "says Grr....")                        mood = "angry"                        print("Have some more rum?")                        ++rum                }        }}pirate = "Blackbeard"rum = 0mood = "angry"print($pirate, "is walking by...")while ( $mood == "angry" ) {        whitewash($pirate)}return "there was much rejoicing"

Build a GUI using YACC
We construct the GUI into a set of widgets that all inherit attributes from the base class. This well outlines the YACC method for analyzing the input. We define a group of rules corresponding to the basic class attributes, and then define rules for each widget, and also define the basic class rules. When the syntax analyzer matches the rules of the widget, we can safely convert the widget pointer to an appropriate class and set the expected attributes. The following is an example of a simple button part:

Yacc_gui.y


%{#include <stdlib.h>#include <stdio.h>#include "widget.h"#include "widget_button.h"#define PARSE_DEBUG(X)(printf X)#define MAX_WIDGET_DEPTH 32static int widget_depth = -1;static Widget *widget_stack[MAX_WIDGET_DEPTH];static Widget *widget;static void StartWidget(Widget *the_widget){widget_stack[widget_depth++] = widget = the_widget;}static void FinishWidget(void){Widget *child;--widget_depth;if ( widget_depth >= 0 ) {child = widget;widget = widget_stack[widget_depth];widget->AddChild(child);}}%}[tokens and types skipped for brevity]%%widget: button{ FinishWidget();  PARSE_DEBUG(("Completed widget/n")); };widget_attribute:widget_area;/* Widget area: x, y, width, height */widget_area:AREA '{' number ',' number ',' number ',' number '}'{ widget->SetArea($3, $5, $7, $9);   PARSE_DEBUG(("Area: %dx%d at (%d,%d)/n", $7, $9, $3, $5)); };/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *//* The button widget */button:button_tag '{' button_attributes '}'{ PARSE_DEBUG(("Completed button/n")); };button_tag:BUTTON name{ StartWidget(new WidgetButton($2));   PARSE_DEBUG(("Starting a button: %s/n", $2));  free($2); };/* The button widget attributes */button_attributes:button_attribute|button_attributes button_attribute;button_attribute:widget|widget_attribute|button_normal_image|button_hover_image;button_normal_image:IMAGE file{ ((WidgetButton *)widget)->LoadNormalImage($2);  PARSE_DEBUG(("Button normal image: %s/n", $2));  free($2); };button_hover_image:HOVERIMAGE file{ ((WidgetButton *)widget)->LoadHoverImage($2);  PARSE_DEBUG(("Button hover image: %s/n", $2));  free($2); };

Gui example
The following is our main menu, using it as an example of a GUI built using this technology:

Main_menu.gui


background "main_menu" {image "main_menu"button "new_game" {area { 32, 80, 370, 64 }image "main_menu-new"hover_image "main_menu-new_hi"#onclick [ new_gui("new_game") ]onclick [ new_gui("character_screen") ]}button "load_game" {area { 32, 152, 370, 64 }image "main_menu-load"hover_image "main_menu-load_hi"onclick [ new_gui("load_game") ]}button "save_game" {area { 32, 224, 370, 64 }image "main_menu-save"hover_image "main_menu-save_hi"onclick [ new_gui("save_game") ]}button "preferences" {area { 32, 296, 370, 64 }image "main_menu-prefs"hover_image "main_menu-prefs_hi"onclick [ new_gui("preferences") ]}button "quit_game" {area { 32, 472, 370, 64 }image "main_menu-quit"hover_image "main_menu-quit_hi"onclick [ quit_game() ]}}

In this screen description, the widgets and properties are analyzed by the syntax analyzer, and the button callback is explained by the script syntax analyzer. New_gui () and quit_game () are internal functions exported to the script mechanism.

Main Menu

Conclusion
Lex and YACC are important tools in the design of our scripts and GUI Design languages. They seem timid at first, but after a while, you will find them easy to use. Please visit our column again next month. We will start to combine them and lead youPirates ho!World.

References

  • Please visitPirates ho!Website
  • You can download the source code of this sample:
    • Eval.tar.gz (script sample source code)
    • Snapshot-043000.tar.gz (game source code snapshot)
  • "Pirates ho! "Library used
    • Sdl_image
    • Sdl_mixer
    • Sdl_ttf
  • Lex & YACC, version 2nd, o'reilly, November 1992
  • DeveloperworksOn Pirates ho! Series:
    • "SDL: make Linux interesting"
    • "Use SDL:" Pirates ho! "Birth"
    • SDL usage, Part 1: "Pirates ho! "Encoding
    • "SDL usage, Part 1: Graphic Design"
  • Describes the SDL usage of SDL APIs. Part 1: "Pirates ho! "Encoding

About the author
Sam lantinga is the author of the Simple DirectMedia Layer (SDL) Library and is now the chief programmer of Loki entertainment software, a company dedicated to producing the best-selling Linux games. He started dealing with Linux and games in 1995 and engaged in various types of doom! Tool porting and Macintosh gamesMaelstromPort to Linux.

Lauren macdonell is a technical writer at skillednursing.com and also "Pirates ho! . While working, writing, or dancing, she takes care of tropical fish.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.