up: Table of contents | prev: Writing Parsers | next: Testing the Generated Parsers

3.2 Building the Parsers

These instructions are for the Microsoft Visual Studio Community Edition and for the x64 configuration.

We have placed each parser to a separate .parser file although this is not mandatory.

Start by creating a text file with an .spg extension to the directory where you have the .parser files.

We have created a file MinilangParsers.spg and placed it in the examples/minilang directory. Here's the contents of the MinilangParsers.spg:

        project MinilangParsers;
        source <TypeParser.parser>;
    

An .spg file contains a project declaration followed by a source declaration for each .parser file. Each declaration is terminated by a semicolon. A project declaration consists of the keyword project followed by the name of the project and a semicolon. A source declaration consists of the keyword source followed by a .parser file path in angle brackets and a semicolon.

Type Parser

Here's the current contents of the TypeParser.parser containing the type parser:

        parser TypeParser
        {
            Type
                ::= INT
                |   BOOL
                |   VOID
                ;
        }
    

Run the following command from the directory that contains the MinilangParsers.spg:

        > spg -v MinilangParsers.spg
    

The SoulNG parser generator spg warns that a uselexer statement is missing from the TypeParser:

        C:\soulng-1.0.0\examples\minilang>spg -v MinilangParsers.spg
        > C:/soulng-1.0.0/examples/minilang/MinilangParsers.spg
        > C:/soulng-1.0.0/examples/minilang/TypeParser.parser
        warning: 'uselexer' statement missing from 'TypeParser' parser.
        ==> C:/soulng-1.0.0/examples/minilang/TypeParser.hpp
        ==> C:/soulng-1.0.0/examples/minilang/TypeParser.cpp
    

The uselexer statement is necessary to connect the parser to the corresponding lexer. The name of the lexer used is MinilangLexer, so we add a uselexer MinilangLexer; statement to the parser, and run the generator again:

        parser TypeParser
        {
            uselexer MinilangLexer;

            Type
                ::= INT
                |   BOOL
                |   VOID
                ;
        }
    
        > spg -v MinilangParsers.spg
    
        C:\soulng-1.0.0\examples\minilang>spg -v MinilangParsers.spg
        > C:/soulng-1.0.0/examples/minilang/MinilangParsers.spg
        > C:/soulng-1.0.0/examples/minilang/TypeParser.parser
        ==> C:/soulng-1.0.0/examples/minilang/TypeParser.hpp
        ==> C:/soulng-1.0.0/examples/minilang/TypeParser.cpp
    

Now the tool does not warn about anything, and has generated some C++ source code, TypeParser.hpp and TypeParser.cpp. Add the generated TypeParser.hpp and TypeParser.cpp files to the minilang project. When trying to compile the Visual Studio compiler generates error: "use of undefined type 'MinilangLexer'". The compiler has not seen the definition of the MinilangLexer class that is used in the TypeParser.cpp. The spg tool recognizes simple #include directives and places them to the start of the generated .cpp file, so we add an include to the start of the TypeParser.parser file:

        #include <minilang/MinilangLexer.hpp>

        parser TypeParser
        {
            uselexer MinilangLexer;

            Type
                ::= INT
                |   BOOL
                |   VOID
                ;
        }
    

Then run the generator again:

        > spg -v MinilangParsers.spg
    

Now when trying to compile the Visual Studio compiler still generates an error: "'INT': undeclared identifier".

The lexer tokens seems to be defined in the MinilangTokens.hpp, so we add another include directive and try again:

        #include <minilang/MinilangLexer.hpp>
        #include <minilang/MinilangTokens.hpp>

        parser TypeParser
        {
            uselexer MinilangLexer;

            Type
                ::= INT
                |   BOOL
                |   VOID
                ;
        }
    

Visual Studio compiler still generates an error: "'INT': undeclared identifier". Looking to the MinilangTokens.hpp reveals the problem: the tokens are inside the "MinilangTokens" namespace, but the TypeParser.cpp does not contain a using directive. The spg recognizes also simple using namespace directives, and adds them to the generated .cpp file, so we add a using directive to the TypeParser.parser:

        #include <minilang/MinilangLexer.hpp>
        #include <minilang/MinilangTokens.hpp>

        using namespace MinilangTokens;

        parser TypeParser
        {
            uselexer MinilangLexer;

            Type
                ::= INT
                |   BOOL
                |   VOID
                ;
        }
    

Then run the generator again:

        > spg -v MinilangParsers.spg
    

Now the code compiles and links without errors.

Literal Parser

Here's the current content of the LiteralParser.parser containing the literal parser:

        parser LiteralParser
        {
            Literal
                ::= BooleanLiteral:booleanLiteral
                |   IntegerLiteral:integerLiteral
                ;

            BooleanLiteral
                ::= TRUE
                |   FALSE
                ;

            IntegerLiteral
                ::= INTLIT
                ;
        }
    

We have added the same uselexer, #include and using declarations as for the type parser:

        #include <minilang/MinilangLexer.hpp>
        #include <minilang/MinilangTokens.hpp>

        using namespace MinilangTokens;

        parser LiteralParser
        {
            uselexer MinilangLexer;

            Literal
                ::= BooleanLiteral:booleanLiteral
                |   IntegerLiteral:integerLiteral
                ;

            BooleanLiteral
                ::= TRUE
                |   FALSE
                ;

            IntegerLiteral
                ::= INTLIT
                ;
        }
    

Add the LiteralParser.parser to the MinilangParsers.spg file:

        project MinilangParsers;
        source <LiteralParser.parser>;
        source <TypeParser.parser>;
    

Then run the spg tool:

        spg -v MinilangParsers.spg
    

Now add the generated LiteralParser.hpp and LiteralParser.cpp to the project.

This time the compilation and linking should succeed without any errors.

Identifier Parser

Here's the changed contents of the IdentifierParser.parser:

        #include <minilang/MinilangLexer.hpp>
        #include <minilang/MinilangTokens.hpp>

        using namespace MinilangTokens;

        parser IdentifierParser
        {
            uselexer MinilangLexer;

            Identifier
                ::= ID
                ;
        }
    

Add the IdentifierParser.parser to the MinilangParsers.spg file:

        project MinilangParsers;
        source <IdentifierParser.parser>;
        source <LiteralParser.parser>;
        source <TypeParser.parser>;
    

Then run the spg tool:

        spg -v MinilangParsers.spg
    

Now add the generated IdentifierParser.hpp and IdentifierParser.cpp to the project, and build the project. No errors expected.

Expression Parser

Here's the changed contents of the ExpressionParser.parser:

        #include <minilang/MinilangLexer.hpp>
        #include <minilang/MinilangTokens.hpp>

        using namespace MinilangTokens;

        parser ExpressionParser
        {
            uselexer MinilangLexer;

            using LiteralParser.Literal;
            using IdentifierParser.Identifier;

            Expression
                ::= EqualityExpression:expr
                ;

            PrimaryExpression
                ::= Literal:literal
                |   Identifier:identifier
                |   LPAREN Expression:expression RPAREN
                ;

            PostfixExpression
                ::= PrimaryExpression:primary (LPAREN ExpressionList:args? RPAREN)*
                ;

            ExpressionList
                ::= Expression:left (COMMA Expression:right)*
                ;

            UnaryExpression
                ::= UnaryOperator:op UnaryExpression:unaryExpr
                |   PostfixExpression:postfixExpr
                ;

            UnaryOperator
                ::= PLUS
                |   MINUS
                |   NOT
                ;

            MultiplicativeExpression
                ::= UnaryExpression:left (MultiplicativeOperator:op UnaryExpression:right)*
                ;

            MultiplicativeOperator
                ::= MUL
                |   DIV
                |   MOD
                ;

            AdditiveExpression
                ::= MultiplicativeExpression:left (AdditiveOperator:op MultiplicativeExpression:right)*
                ;

            AdditiveOperator
                ::= PLUS
                |   MINUS
                ;

            RelationalExpression
                ::= AdditiveExpression:left (RelationalOperator:op AdditiveExpression:right)*
                ;

            RelationalOperator
                ::= LESS
                |   GREATER
                |   LEQ
                |   GEQ
                ;

            EqualityExpression
                ::= RelationalExpression:left (EqualityOperator:op RelationalExpression:right)*
                ;

            EqualityOperator
                ::= EQ
                |   NEQ
                ;
        }
    

Add the ExpressionParser.parser to the MinilangParsers.spg file:

        project MinilangParsers;
        source <ExpressionParser.parser>;
        source <IdentifierParser.parser>;
        source <LiteralParser.parser>;
        source <TypeParser.parser>;
    

Then run the spg tool:

        spg -v MinilangParsers.spg
    

Add the generated ExpressionParser.hpp and ExpressionParser.cpp to the project, and build the project.

Add this time Visual Studio generates an error: 'LiteralParser': is not a class or namespace name. The expression parser uses the LiteralParser and the IdentifierParser, but the compiler has seen no declarations for them. We need to add the following #include declarations to the ExpressionParser.parser, run spg, and then build again:

        #include <minilang/LiteralParser.hpp>
        #include <minilang/IdentifierParser.hpp>
        // ...
    

Now the project builds again.

Statement Parser

Here's the changed contents of the StatementParser.parser:

        #include <minilang/ExpressionParser.hpp>
        #include <minilang/TypeParser.hpp>
        #include <minilang/IdentifierParser.hpp>
        #include <minilang/MinilangLexer.hpp>
        #include <minilang/MinilangTokens.hpp>

        using namespace MinilangTokens;

        parser StatementParser
        {
            uselexer MinilangLexer;

            using ExpressionParser.Expression;
            using TypeParser.Type;
            using IdentifierParser.Identifier;

            Statement
                ::= IfStatement:ifS
                |   WhileStatement:whileS
                |   ReturnStatement:returnS
                |   ConstructionStatement:constructionS
                |   AssignmentStatement:assignmentS
                |   CompoundStatement:compoundS
                ;

            IfStatement
                ::= IF LPAREN Expression:condition RPAREN Statement:thenS (ELSE Statement:elseS)?
                ;

            WhileStatement
                ::= WHILE LPAREN Expression:condition RPAREN Statement:statement
                ;

            ReturnStatement
                ::= RETURN Expression:returnValue? SEMICOLON
                ;

            ConstructionStatement
                ::= Type:type Identifier:variableName ASSIGN Expression:value SEMICOLON
                ;

            AssignmentStatement
                ::= Identifier:variableName ASSIGN Expression:value SEMICOLON
                ;

            CompoundStatement
                ::= LBRACE Statement:statement* RBRACE
                ;
        }
    

This time we have added the needed #includes for ExpressionParser.hpp, TypeParser.hpp and IdentifierParser.hpp.

Add the StatementParser.parser to the MinilangParsers.spg file:

        project MinilangParsers;
        source <ExpressionParser.parser>;
        source <IdentifierParser.parser>;
        source <LiteralParser.parser>;
        source <StatementParser.parser>;
        source <TypeParser.parser>;
    

Run the spg tool and build. No errors expected.

        spg -v MinilangParsers.spg
    

Function Parser

Here's the changed contents of the FunctionParser.parser:

        #include <minilang/TypeParser.hpp>
        #include <minilang/IdentifierParser.hpp>
        #include <minilang/StatementParser.hpp>
        #include <minilang/MinilangLexer.hpp>
        #include <minilang/MinilangTokens.hpp>

        using namespace MinilangTokens;

        parser FunctionParser
        {
            uselexer MinilangLexer;

            using TypeParser.Type;
            using IdentifierParser.Identifier;
            using StatementParser.CompoundStatement;

            Function
                ::= Type:returnType Identifier:functionName LPAREN ParameterList:params? RPAREN CompoundStatement:functionBody
                ;

            ParameterList
                ::= Parameter:left (COMMA Parameter:right)*
                ;

            Parameter
                ::= Type:type Identifier:name
                ;
        }
    

Source File Parser

A source file parser is different from the other parsers that it is expected to parse an entire file content. We call it a main parser.

A main parser is declared by including a main statement to the parser definition. A main statement consists of the keyword main followed by a semicolon.

Nothing prevents from including the main statement in every parser, so that they could be tested separately.

Here's the changed contents of the SourceFileParser.parser with a main statement:

        #include <minilang/FunctionParser.hpp>
        #include <minilang/MinilangLexer.hpp>
        #include <minilang/MinilangTokens.hpp>

        using namespace MinilangTokens;

        parser SourceFileParser
        {
            uselexer MinilangLexer;
            main;

            using FunctionParser.Function;

            SourceFile
                ::= Function:function*
                ;
        }
    

We have added these files to the MinilangParsers.spg, run the spg tool, and built:

        C:\soulng-1.0.0\examples\minilang>spg -v MinilangParsers.spg
        > C:/soulng-1.0.0/examples/minilang/MinilangParsers.spg
        > C:/soulng-1.0.0/examples/minilang/ExpressionParser.parser
        ==> C:/soulng-1.0.0/examples/minilang/ExpressionParser.hpp
        ==> C:/soulng-1.0.0/examples/minilang/ExpressionParser.cpp
        > C:/soulng-1.0.0/examples/minilang/FunctionParser.parser
        ==> C:/soulng-1.0.0/examples/minilang/FunctionParser.hpp
        ==> C:/soulng-1.0.0/examples/minilang/FunctionParser.cpp
        > C:/soulng-1.0.0/examples/minilang/IdentifierParser.parser
        ==> C:/soulng-1.0.0/examples/minilang/IdentifierParser.hpp
        ==> C:/soulng-1.0.0/examples/minilang/IdentifierParser.cpp
        > C:/soulng-1.0.0/examples/minilang/LiteralParser.parser
        ==> C:/soulng-1.0.0/examples/minilang/LiteralParser.hpp
        ==> C:/soulng-1.0.0/examples/minilang/LiteralParser.cpp
        > C:/soulng-1.0.0/examples/minilang/SourceFileParser.parser
        ==> C:/soulng-1.0.0/examples/minilang/SourceFileParser.hpp
        ==> C:/soulng-1.0.0/examples/minilang/SourceFileParser.cpp
        > C:/soulng-1.0.0/examples/minilang/StatementParser.parser
        ==> C:/soulng-1.0.0/examples/minilang/StatementParser.hpp
        ==> C:/soulng-1.0.0/examples/minilang/StatementParser.cpp
        > C:/soulng-1.0.0/examples/minilang/TypeParser.parser
        ==> C:/soulng-1.0.0/examples/minilang/TypeParser.hpp
        ==> C:/soulng-1.0.0/examples/minilang/TypeParser.cpp
    

Now the entire project is expected to build without errors. Next we will see how to test the generated parsers...

up: Table of contents | prev: Writing Parsers | next: Testing the Generated Parsers