alpag.net manual
Examples / Example: Calculator
< Configuration | Options reference >

Example: Calculator

Below is a complete example of calculator application. It includes both a lexer and a parser (as separate classes). Three code files are produced: one for lexer, one for parser and third one with common definitions. Code of main application is included further below

Alpag input file.

// Namespace to be used for all classes
%option Code.Namespace "Calc"
// Place common definitions in separate file
%option Code.Base.OwnFile true

// Name for parser class
%option Parser.Code.Parser.ClassName "CalcParser"
// Only single value type will be used
%option Parser.Value.FieldType { double }

// Input will be read from text buffer
%option Lexer.In.Format Chars

// Name of parser class
%option Lexer.Code.Lexer.ClassName "CalcLexer"

// Lexer invocation returns double value
%option Lexer.Code.NextTokenFuncArgs Custom
%option Lexer.Code.NextTokenFuncArgsCustom { ref double resValue }

%code parser_fields {
// Reference to lexer class
public CalcLexer lexer;
// Final result
public double result;
}

%code lexer_fields {
// Input text entered by user
protected string InputText = string.Empty;
// Current lexer position in input text
protected int InputOff;

public void SetInputText( string value )
{
InputText = value;
InputOff = 0;
}
}

%code read_input_chars {
// Trivial implementation, returns char at a time
if( InputOff < InputText.Length )
{
buffer[offset++] = InputText[InputOff++];
return 1;
}
return 0;
}

%code parser_next_token {
// Invoke lexer, assign return value to token
valueData.Value = 0;
symbol = lexer.NextToken( ref valueData.Value );
}

// Parser grammar
%%pdefs
%token NUMBER
%token BR_OPEN "("
%token BR_CLOSE ")"
// Left associative tokens with higher precedence
%left PLUS "+" MINUS "-"
// Left associative tokens with even higher precedence
%left MUL "*" DIV "/"

%%prules
RESULT: EXPR { result = $1; };
EXPR:
NUMBER { $$ = $1; } |
"(" EXPR ")" { $$ = $2; } |
EXPR "+" EXPR { $$ = $1 + $3; } |
EXPR "-" EXPR { $$ = $1 - $3; } |
EXPR "*" EXPR { $$ = $1 * $3; } |
EXPR "/" EXPR {
double div = $3;
if( div == 0 )
$$ = double.NaN;
else
$$ = $1 / $3;
};

// Lexer grammar
%%lrules
[0-9]+(\.[0-9]+)? {
resValue = Double.Parse(
TokenValueGetString(), System.Globalization.CultureInfo.InvariantCulture
);
return CalcParser.NUMBER;
}

\+ { return CalcParser.PLUS; }
\- { return CalcParser.MINUS; }
\* { return CalcParser.MUL; }
\/ { return CalcParser.DIV; }
\( { return CalcParser.BR_OPEN; }
\) { return CalcParser.BR_CLOSE; }

[\ \t]+ // skip space

Application code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Calc
{
class Program
{
static void Main( string[] args )
{
CalcLexer lexer = new CalcLexer();
CalcParser parser = new CalcParser();
parser.lexer = lexer;

// Keep reading input lines until empty line is entered
for( ; ; )
{
string line = Console.ReadLine();
if( string.IsNullOrEmpty( line ) )
break;
// New lexing/parsing session launched each time
lexer.ResetLexer();
parser.ResetParser();
lexer.SetInputText( line );
int status = parser.Parse();
if( status != 0 )
{
// Parsing failed
Console.WriteLine( "Parse error" );
}
else
{
// Print result
double result = parser.result;
if( double.IsNaN( result ) )
Console.WriteLine( "Calculation error" );
else
Console.WriteLine( "=" + result );
}
}

}
}
}
< Configuration | Options reference >
Alpag Manual