- Token definitions:
tokens calculator.token
{
(NUMBER, "number"), (VARIABLE, "variable"), (PI, "'pi'"), (PLUS, "+"), (MINUS, "-"), (TIMES, "*"), (DIVIDES, "/"),
(ASSIGN, "="), (LPAREN, "("), (RPAREN, ")")
}
- Keywords:
import calculator.token;
keywords calculator.keyword
{
("pi", PI)
}
- Regular expressions:
expressions calculator.expr
{
ws = "[\t ]";
separators = "{ws}+";
dec_digit = "[0-9]";
fraction = "{dec_digit}*\.{dec_digit}+|{dec_digit}+\.";
sign = "\+|-";
exponent = "(e|E){sign}?{dec_digit}+";
number = "{fraction}{exponent}?|{dec_digit}+{exponent}?";
variable = "[a-zA-Z_][a-zA-Z_0-9]*";
}
- Lexer:
export module calculator.lexer;
import calculator.token;
import calculator.keyword;
import calculator.expr;
lexer CalculatorLexer
{
rules
{
"{separators}" {}
"{number}" { return NUMBER; }
"{variable}"
{
std::int64_t kw = lexer.GetKeywordToken(lexer.CurrentToken().match);
if (kw == soul::lexer::INVALID_TOKEN) return VARIABLE; else return kw;
}
"\+" { return PLUS; }
"-" { return MINUS; }
"\*" { return TIMES; }
"/" { return DIVIDES; }
"=" { return ASSIGN; }
"\(" { return LPAREN; }
"\)" { return RPAREN; }
}
}
- Lexer project file:
project calculator.slg;
tokens <calculator.token>;
keywords <calculator.keyword>;
expressions <calculator.expr>;
lexer <calculator.lexer>;
- Calculator interface:
export module calculator;
import std;
export namespace calculator
{
enum class Operator
{
plus, minus, times, divides
};
class Memory
{
public:
Memory();
void SetValue(const std::u32string& variableName, double value);
double GetValue(const std::u32string& variableName) const;
private:
std::map<std::u32string, double> variableMap;
};
}
- Calculator implementation:
module calculator;
import util;
namespace calculator
{
Memory::Memory()
{
}
void Memory::SetValue(const std::u32string& variableName, double value)
{
variableMap[variableName] = value;
}
double Memory::GetValue(const std::u32string& variableName) const
{
auto it = variableMap.find(variableName);
if (it != variableMap.end())
{
return it->second;
}
else
{
throw std::runtime_error("variable '" + util::ToUtf8(variableName) + "' not found");
}
}
}
- Parser:
export module calculator.parser;
[interface]import calculator;
[implementation]import calculator.lexer;
[implementation]import calculator.token;
parser CalculatorParser
{
lexer calculator::lexer::CalculatorLexer<char32_t>;
main;
expression(calculator::Memory* memory) : double
::= assignment(memory):a{ return a; }
| term(memory):t{ return t; }
;
assignment(calculator::Memory* memory, var std::u32string variableName) : double
::= VARIABLE{ variableName = lexer.GetToken(pos).ToString(); }
ASSIGN
term(memory):t{ double value = t; memory->SetValue(variableName, value); return value; }
;
term(calculator::Memory* memory, var double value) : double
::=
(
factor(memory):left{ value = left; }
(
weak_operator:op factor(memory):right
{
switch (op)
{
case calculator::Operator::plus:
{
value += right;
break;
}
case calculator::Operator::minus:
{
value -= right;
break;
}
}
}
)*
)
{
return value;
}
;
weak_operator : calculator::Operator
::= PLUS{ return calculator::Operator::plus; }
| MINUS{ return calculator::Operator::minus; }
;
factor(calculator::Memory* memory, var double value) : double
::=
(
unary(memory):left{ value = left; }
(
strong_operator:op unary(memory):right
{
switch (op)
{
case calculator::Operator::times:
{
value *= right;
break;
}
case calculator::Operator::divides:
{
value /= right;
break;
}
}
}
)*
)
{
return value;
}
;
strong_operator : calculator::Operator
::= TIMES{ return calculator::Operator::times; }
| DIVIDES{ return calculator::Operator::divides; }
;
unary(calculator::Memory* memory) : double
::= MINUS unary(memory):e{ return -e;}
| primary(memory):p{ return p; }
;
primary(calculator::Memory* memory) : double
::= NUMBER{ return lexer.GetToken(pos).ToDouble(); }
| VARIABLE{ return memory->GetValue(lexer.GetToken(pos).ToString());}
| PI{ return std::numbers::pi; }
| LPAREN term(memory):t RPAREN{ return t; }
;
}
- Parser project file:
project calculator.spg;
parser <calculator.parser>;
tokens <calculator.token>;
- Main program:
import std;
import calculator;
import calculator.lexer;
import calculator.parser;
import util;
int main()
{
try
{
calculator::Memory memory;
while (true)
{
std::cout << "enter expression:" << "\n";
std::string expr;
std::getline(std::cin, expr);
if (expr == "exit") break;
std::u32string e = util::ToUtf32(expr);
auto lexer = calculator::lexer::MakeLexer(e.c_str(), e.c_str() + e.length(), "<expr>");
using LexerType = decltype(lexer);
try
{
std::cout << calculator::parser::CalculatorParser<LexerType>::Parse(lexer, &memory) << "\n";
}
catch (const std::exception& ex)
{
std::cout << ex.what() << "\n";
}
}
std::cout << "bye" << "\n";
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << "\n";
return 1;
}
}