calc example
This commit is contained in:
parent
8eda2146d9
commit
1e829b5c1d
|
@ -1,3 +1,4 @@
|
|||
add_subdirectory(json)
|
||||
add_subdirectory(calc)
|
||||
|
||||
add_custom_target(examples DEPENDS json)
|
||||
add_custom_target(examples DEPENDS json calc)
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
|
@ -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")
|
|
@ -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
|
|
@ -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];
|
|
@ -0,0 +1,7 @@
|
|||
#include "parser.h"
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
calc::Parser p(CalcLexer{std::cin});
|
||||
p.parse();
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
Loading…
Reference in New Issue