calc example

This commit is contained in:
Robin Jadoul 2017-01-28 14:29:08 +01:00
parent 8eda2146d9
commit 1e829b5c1d
9 changed files with 508 additions and 1 deletions

View File

@ -1,3 +1,4 @@
add_subdirectory(json)
add_subdirectory(calc)
add_custom_target(examples DEPENDS json)
add_custom_target(examples DEPENDS json calc)

102
examples/calc/AST.cpp Normal file
View File

@ -0,0 +1,102 @@
#include "AST.h"
#include <cmath>
namespace calc {
Number::Number(double d) : m_val(d) {}
double Number::eval(const Variables&, const Functions&) const {
return m_val;
}
Var::Var(std::string name) : m_name(name) {}
double Var::eval(const Variables& vars, const Functions&) const {
auto v = vars.find(m_name);
if (v != vars.end())
return v->second;
return 0;
}
std::string Var::getName() const {
return m_name;
}
Binop::Binop(std::unique_ptr<AST>&& left, std::string op, std::unique_ptr<AST>&& right)
: m_left(std::move(left)), m_right(std::move(right)), m_op(op) {}
double Binop::eval(const Variables& vars, const Functions& funs) const {
double left = m_left->eval(vars, funs);
double right = m_right->eval(vars, funs);
if (m_op == "+")
return left + right;
else if (m_op == "-")
return left - right;
else if (m_op == "*")
return left * right;
else if (m_op == "/")
return left / right;
else if (m_op == "^")
return std::pow(left, right);
return 0;
}
Unop::Unop(std::string op, std::unique_ptr<AST>&& operand) : m_op(op), m_operand(std::move(operand)) {}
double Unop::eval(const Variables& vars, const Functions& funs) const {
if (m_op == "-")
return -m_operand->eval(vars, funs);
return 0;
}
double FormalParameters::eval(const Variables&, const Functions&) const {
return 0;
}
void FormalParameters::push_back(std::unique_ptr<AST>&& arg) {
m_args.emplace_back(std::move(arg));
}
std::deque<std::unique_ptr<AST>>::const_iterator FormalParameters::begin() {
return m_args.begin();
}
std::deque<std::unique_ptr<AST>>::const_iterator FormalParameters::end() {
return m_args.end();
}
double FunctionArguments::eval(const Variables&, const Functions&) const {
return 0;
}
std::unique_ptr<AST>& FunctionArguments::operator[](std::size_t idx) {
return m_args[idx];
}
void FunctionArguments::push_back(std::unique_ptr<AST>&& arg) {
m_args.push_back(std::move(arg));
}
std::size_t FunctionArguments::size() const {
return m_args.size();
}
FunctionCall::FunctionCall(std::string name, std::unique_ptr<FunctionArguments>&& arguments) : m_name(name), m_arguments(std::move(arguments)) {}
double FunctionCall::eval(const Variables& vars, const Functions& funs) const {
auto fp = funs.find(m_name);
if (fp == funs.end())
return 0;
Function& fun = *dynamic_cast<Function*>((fp->second.get()));
if (m_arguments->size() != fun.getParams().size()) return 0;
Variables newVars = vars;
std::size_t i = 0;
for (const std::string& param : fun.getParams()) {
newVars[param] = (*m_arguments)[i]->eval(vars, funs);
i++;
}
return fun.eval(newVars, funs);
}
Function::Function(std::deque<std::string> formalParams, std::unique_ptr<AST>&& body)
: m_formalParams(std::move(formalParams)), m_body(std::move(body)) {}
double Function::eval(const Variables& vars, const Functions& funs) const {
return m_body->eval(vars, funs);
}
const std::deque<std::string>& Function::getParams() const {
return m_formalParams;
}
} /* calc */

111
examples/calc/AST.h Normal file
View File

@ -0,0 +1,111 @@
#pragma once
#ifndef CALC_AST_H
#define CALC_AST_H
#include <deque>
#include <map>
#include <memory>
#include <string>
namespace calc {
class AST;
using Variables = std::map<std::string, double>;
using Functions = std::map<std::string, std::unique_ptr<AST>>;
class AST {
public:
virtual double eval(const Variables&, const Functions&) const = 0;
};
class Number : public AST {
public:
Number(double d);
double eval(const Variables&, const Functions&) const override;
private:
double m_val;
};
class Var : public AST {
public:
Var(std::string name);
double eval(const Variables& ctx, const Functions&) const override;
std::string getName() const;
private:
std::string m_name;
};
class Binop : public AST {
public:
Binop(std::unique_ptr<AST>&& left, std::string op, std::unique_ptr<AST>&& right);
double eval(const Variables& ctx, const Functions&) const override;
private:
std::unique_ptr<AST> m_left;
std::unique_ptr<AST> m_right;
std::string m_op;
};
class Unop : public AST {
public:
Unop(std::string op, std::unique_ptr<AST>&& operand);
double eval(const Variables&, const Functions&) const override;
private:
std::string m_op;
std::unique_ptr<AST> m_operand;
};
class FormalParameters : public AST {
public:
double eval(const Variables&, const Functions&) const override;
void push_back(std::unique_ptr<AST>&& arg);
std::deque<std::unique_ptr<AST>>::const_iterator begin();
std::deque<std::unique_ptr<AST>>::const_iterator end();
private:
std::deque<std::unique_ptr<AST>> m_args;
};
class FunctionArguments : public AST {
public:
double eval(const Variables&, const Functions&) const override;
std::unique_ptr<AST>& operator[](std::size_t idx);
void push_back(std::unique_ptr<AST>&& arg);
std::size_t size() const;
private:
std::deque<std::unique_ptr<AST>> m_args;
};
class FunctionCall : public AST {
public:
FunctionCall(std::string name, std::unique_ptr<FunctionArguments>&& arguments);
double eval(const Variables&, const Functions&) const override;
private:
std::string m_name;
std::unique_ptr<FunctionArguments> m_arguments;
};
class Function : public AST {
public:
Function(std::deque<std::string> formalParams, std::unique_ptr<AST>&& body);
double eval(const Variables&, const Functions&) const override;
const std::deque<std::string>& getParams() const;
private:
std::deque<std::string> m_formalParams;
std::unique_ptr<AST> m_body;
};
} /* calc */
#endif //CALC_AST_H

View File

@ -0,0 +1,19 @@
add_custom_command(DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/calcLexer.lxs"
COMMAND "${LEXESIS_EXE}" ARGS -d "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/calcLexer.lxs"
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/calcLexer.h" "${CMAKE_CURRENT_BINARY_DIR}/calcLexer.cpp")
find_program(PARSODUS_EXE Parsodus PATH "${CMAKE_CURRENT_BINARY_DIR}/../../bin")
add_custom_command(DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/calcParser.pds"
COMMAND "${PARSODUS_EXE}" ARGS -d "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/calcParser.pds"
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/calcParser.h" "${CMAKE_CURRENT_BINARY_DIR}/calcParser.cpp")
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
add_executable(calc
EXCLUDE_FROM_ALL
main.cpp
parser.cpp
AST.cpp
"${CMAKE_CURRENT_BINARY_DIR}/CalcLexer.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/calcParser.cpp")

View File

@ -0,0 +1,15 @@
COMMA = ,
FN = fn
LPAREN = \(
RPAREN = \)
SEMICOLON = ;
ARROW = ->
PLUS = \+
MINUS = -
TIMES = \*
DIVIDE = /
EXPONENT = ^
ASSIGN = =
NUM = (0|[1-9][0-9]*)(\.[0-9]+)?
IDENT = [a-zA-Z_][a-zA-Z0-9_]*
ignore = \n|\r| |\t

View File

@ -0,0 +1,67 @@
parser: LALR(1)
terminals:
"NUM"
"COMMA"
"FN"
"LPAREN"
"RPAREN"
"SEMICOLON"
"ARROW"
"IDENT"
"PLUS"
"MINUS"
"TIMES"
"DIVIDE"
"EXPONENT"
"ASSIGN"
precedence:
right "EXPONENT"
left "TIMES" "DIVIDE"
left "PLUS" "MINUS"
start: <start>
grammar:
<start> ::= <toplevel> <start> [toplevel]
| [toplevel]
;
<toplevel> ::= <toplevel_expr> "SEMICOLON" [toplevel]
| <functiondef> "SEMICOLON" [toplevel]
| <assign> "SEMICOLON" [toplevel]
| <error> "SEMICOLON" [toplevel]
;
<toplevel_expr> ::= <expr> [toplevel];
<expr> ::= <expr> "PLUS" <expr> [binop]
| <expr> "MINUS" <expr> [binop]
| <expr> "TIMES" <expr> [binop]
| <expr> "DIVIDE" <expr> [binop]
| <expr> "EXPONENT" <expr> [binop]
| "IDENT" [expr_simple]
| "NUM" [expr_simple]
| "MINUS" <expr> [umin, right 1]
| "LPAREN" <expr> "RPAREN" [parenthesized]
| "IDENT" "LPAREN" <opt_arguments> "RPAREN" [functioncall]
;
<opt_arguments> ::= <arguments> [opt_arguments]
| [opt_arguments]
;
<arguments> ::= <expr> [arguments]
| <arguments> "COMMA" <expr> [arguments]
;
<functiondef> ::= "FN" "IDENT" "LPAREN" <opt_idents> "RPAREN" "ARROW" <expr> [functiondef];
<opt_idents> ::= <idents> [opt_idents]
| [opt_idents]
;
<idents> ::= "IDENT" [idents]
| <idents> "COMMA" "IDENT" [idents]
;
<assign> ::= "IDENT" "ASSIGN" <expr> [assign];

7
examples/calc/main.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "parser.h"
#include <iostream>
int main() {
calc::Parser p(CalcLexer{std::cin});
p.parse();
}

143
examples/calc/parser.cpp Normal file
View File

@ -0,0 +1,143 @@
#include "parser.h"
#include <iostream>
namespace calc {
Parser::Parser(CalcLexer lex) : m_lex(lex)
{}
Parser::Token Parser::lex() {
while (true) {
try {
CalcLexer::Token orig = m_lex.nextToken();
if (orig.type == CalcLexer::NUM) {
return { calcParser_Symbol::T_NUM, std::make_unique<Number>(std::stod(orig.content)) };
} else if (orig.type == CalcLexer::IDENT) {
return { calcParser_Symbol::T_IDENT, std::make_unique<Var>(orig.content) };
} else {
return { static_cast<calcParser_Symbol>(orig.type), nullptr };
}
}
catch (CalcLexer::NoMoreTokens) {
return { calcParser_Symbol::T_EOF, nullptr };
}
catch (CalcLexer::NoMatch) {
std::cerr << "Syntax error: unrecognized token" << std::endl;
}
}
}
Parser::Value Parser::reduce_toplevel(std::deque<Token> subparts) {
if (subparts.empty()) return nullptr;
if (subparts[0].symbol == calcParser_Symbol::V_expr) {
std::cout << "=> " << subparts[0].value->eval(m_variables, m_functions) << std::endl;
} else if (subparts[0].symbol == calcParser_Symbol::V_error) {
std::cout << "=> <syntax error>" << std::endl;
}
return nullptr;
}
Parser::Value Parser::reduce_binop(std::deque<Token> subparts) {
std::string op;
switch (subparts[1].symbol) {
case calcParser_Symbol::T_PLUS:
op = "+";
break;
case calcParser_Symbol::T_MINUS:
op = "-";
break;
case calcParser_Symbol::T_TIMES:
op = "*";
break;
case calcParser_Symbol::T_DIVIDE:
op = "/";
break;
case calcParser_Symbol::T_EXPONENT:
op = "^";
break;
default:
break;
}
return std::make_unique<Binop>(std::move(subparts[0].value), op, std::move(subparts[2].value));
}
Parser::Value Parser::reduce_expr_simple(std::deque<Token> subparts) {
return std::move(subparts[0].value);
}
Parser::Value Parser::reduce_umin(std::deque<Token> subparts) {
return std::make_unique<Unop>("-", std::move(subparts[1].value));
}
Parser::Value Parser::reduce_parenthesized(std::deque<Token> subparts) {
return std::move(subparts[1].value);
}
Parser::Value Parser::reduce_functioncall(std::deque<Token> subparts) {
auto args = dynamic_cast<FunctionArguments*>(subparts[2].value.get());
subparts[2].value.release();
return std::make_unique<FunctionCall>(dynamic_cast<Var*>(subparts[0].value.get())->getName(), std::unique_ptr<FunctionArguments>(args));
}
Parser::Value Parser::reduce_opt_arguments(std::deque<Token> subparts) {
if (subparts.size()) {
return std::move(subparts[0].value);
} else {
return std::make_unique<FunctionArguments>();
}
}
Parser::Value Parser::reduce_arguments(std::deque<Token> subparts) {
std::unique_ptr<FunctionArguments> args;
if (subparts.size() > 1) {
args = std::unique_ptr<FunctionArguments>(dynamic_cast<FunctionArguments*>(subparts[0].value.get()));
subparts[0].value.release();
} else {
args = std::make_unique<FunctionArguments>();
}
args->push_back(std::move(subparts[subparts.size() - 1].value));
return args;
}
Parser::Value Parser::reduce_functiondef(std::deque<Token> subparts) {
std::string name = dynamic_cast<Var*>(subparts[1].value.get())->getName();
std::deque<std::string> formalParams;
FormalParameters* fp = dynamic_cast<FormalParameters*>(subparts[3].value.get());
for (const std::unique_ptr<AST>& param : *fp) {
formalParams.push_back(dynamic_cast<Var*>(param.get())->getName());
}
m_functions[name] = std::make_unique<Function>(std::move(formalParams), std::move(subparts[6].value));
std::cout << "=> Function " << name << " defined." << std::endl;
return nullptr;
}
Parser::Value Parser::reduce_opt_idents(std::deque<Token> subparts) {
if (subparts.size()) {
return std::move(subparts[0].value);
} else {
return std::make_unique<FormalParameters>();
}
}
Parser::Value Parser::reduce_idents(std::deque<Token> subparts) {
std::unique_ptr<FormalParameters> args;
if (subparts.size() > 1) {
args = std::unique_ptr<FormalParameters>(dynamic_cast<FormalParameters*>(subparts[0].value.get()));
subparts[0].value.release();
} else {
args = std::make_unique<FormalParameters>();
}
args->push_back(std::move(subparts[subparts.size() - 1].value));
return args;
}
Parser::Value Parser::reduce_assign(std::deque<Token> subparts) {
std::string name = dynamic_cast<Var*>(subparts[0].value.get())->getName();
m_variables[name] = subparts[2].value->eval(m_variables, m_functions);
std::cout << "=> " << name << " = " << m_variables[name] << std::endl;
return nullptr;
}
} /* calc */

42
examples/calc/parser.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#ifndef CALC_PARSER_H
#define CALC_PARSER_H
#include "calcParser.h"
#include "CalcLexer.h"
#include "AST.h"
#include <map>
#include <memory>
#include <string>
namespace calc {
class Parser : public calcParser<std::unique_ptr<AST>> {
public:
Parser(CalcLexer lex);
protected:
using Value = std::unique_ptr<AST>;
Token lex() override;
Value reduce_arguments(std::deque<Token> subparts) override;
Value reduce_assign(std::deque<Token> subparts) override;
Value reduce_binop(std::deque<Token> subparts) override;
Value reduce_expr_simple(std::deque<Token> subparts) override;
Value reduce_functioncall(std::deque<Token> subparts) override;
Value reduce_functiondef(std::deque<Token> subparts) override;
Value reduce_idents(std::deque<Token> subparts) override;
Value reduce_opt_arguments(std::deque<Token> subparts) override;
Value reduce_opt_idents(std::deque<Token> subparts) override;
Value reduce_parenthesized(std::deque<Token> subparts) override;
Value reduce_toplevel(std::deque<Token> subparts) override;
Value reduce_umin(std::deque<Token> subparts) override;
private:
CalcLexer m_lex;
std::map<std::string, double> m_variables;
std::map<std::string, std::unique_ptr<AST>> m_functions;
};
} /* calc */
#endif //CALC_PARSER_H