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.
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.
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.
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.
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.
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
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
;
}
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