• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed Usuwanie drzewa (AST), czy wykorzystać inteligentne wskaźniki? Jeżeli tak to jak?

Object Storage Arubacloud
+1 głos
355 wizyt
pytanie zadane 16 grudnia 2018 w C i C++ przez Jakub 0 Pasjonat (23,120 p.)
zamknięte 20 grudnia 2018 przez Jakub 0

Witam, pisząc własny parser napotkałem się z pierwszymi sporymi dla mnie do pokonania problemami. Początkowo uczyłem się z tego: http://informatyka.wroc.pl/node/391

Usuwanie tam drzewa w razie wystąpienia błędu w wyrażeniu prezentuje się tam tak: http://informatyka.wroc.pl/node/431?page=0,3

Ja nie tak dawno poznałem inteligentne wskaźniki i zacząłem się zastanawiać czy ich nie można wykorzystać, tak wyglądają przykładowo u mnie definicje klas reprezentujących drzewo:

#ifndef EXP_H_
#define EXP_H_

#include <string>
#include <stdexcept>
#include <map>
#include <memory>

///--------------------------------------------------------------------

using Memory = std::map<std::string, double>;

//////////////////////////// EXPRESSION //////////////////////////////////////////

class AST_Exp {
public:
	virtual double Solve(Memory& vars) = 0;
};

///--------------------------------------------------------------------

class Constant : public AST_Exp {
private:
	const double m_constant;
public:
	Constant(double constant) : m_constant(constant) {}
	virtual double Solve(Memory& vars);
};

///--------------------------------------------------------------------

class BinaryOperator : public AST_Exp {
private:
	char m_operator;
	std::unique_ptr<AST_Exp> m_left;
	std::unique_ptr<AST_Exp> m_right;
public:
	BinaryOperator(char operat, AST_Exp* left, AST_Exp* right)
		: m_operator(operat), m_left(left), m_right(right) {}
	virtual double Solve(Memory& vars);
};

///--------------------------------------------------------------------

class Variable : public AST_Exp {
private:
	std::string m_name;
public:
	Variable(const std::string& variable) : m_name(variable) {}
	virtual double Solve(Memory& vars);
};

//////////////////////////// PROGRAM /////////////////////////////////////////////

#endif

Jak widać użyłem tu szablonu klasy std::unique_ptr.

To jednak nie koniec problemów, mamy też klasę parsera:

#ifndef PARSER_H_
#define PARSER_H_
#include "program.h"
#include <string>

///--------------------------------------------------------------------

class Parser {
private:
	const char EOS = 0;
	std::string m_expression;
	unsigned pointer;

private:
	void skip_whitespace();
	char look_ahead();

public:

	Parser(std::string input);
	AST_Exp* parse_expression(); //start
	AST_Exp* parse_logic(); //1 - parsuj zbiór porównań jakim jest wyrażenie
	AST_Exp* parse_sum(); //2 - parsuj sume ( z sum składają się porównania )
	AST_Exp* parse_mult(); //3 - parsuj skladnik ( ze składników składają się sumy )
	AST_Exp* parse_term(); //4 - parsuj czynnik ( z czynników składają się składniki )

	AST_Exp* parse_constant();
	AST_Exp* parse_variable();
	AST_Exp* parse_paren();
};

///--------------------------------------------------------------------

#endif 

i definicje jej metod:

#include <string>
#include <cctype>
#include <stdexcept>
#include <sstream>
#include "program.h"
#include "Parser.h"

///--------------------------------------------------------------------

Parser::Parser(std::string input)
	: pointer(0), m_expression(input) {
	m_expression.push_back(EOS);
}

///--------------------------------------------------------------------

void Parser::skip_whitespace() {
	while (isspace(m_expression[pointer]))
		++pointer;
}

///--------------------------------------------------------------------

char Parser::look_ahead() {
	skip_whitespace();
	return m_expression[pointer];
}

///--------------------------------------------------------------------

AST_Exp* Parser::parse_expression() {
	AST_Exp* res = parse_logic();
	if (look_ahead() == EOS)
		return res;
	else
		throw std::logic_error("bad code structure");
}

///--------------------------------------------------------------------

AST_Exp* Parser::parse_logic() {
	AST_Exp* res = parse_sum();
	char c = look_ahead();
	while (c == '=' || c == '>' || c == '<' || c == '!') {
		++pointer;
		res = new BinaryOperator(c, res, parse_sum());
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

AST_Exp* Parser::parse_sum() {
	AST_Exp* res = parse_mult();
	char c = look_ahead();
	while (c == '+' || c == '-') {
		++pointer;
		res = new BinaryOperator(c, res, parse_mult());
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

AST_Exp* Parser::parse_mult() {
	AST_Exp* res = parse_term();
	char c = look_ahead();
	while (c == '*' || c == '/' || c == '%' || c == '^') {
		++pointer;
		res = new BinaryOperator(c, res, parse_term());
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

AST_Exp* Parser::parse_term() {
	char c = look_ahead();
	if (std::isdigit(c))
		return parse_constant();
	else if (std::isalpha(c))
		return parse_variable();
	else if (c == '(')
		return parse_paren();
	else
		throw std::logic_error("undefined character");
}

///--------------------------------------------------------------------

AST_Exp* Parser::parse_constant() {
	std::string num;
	while (std::isdigit(m_expression[pointer]) || m_expression[pointer] == '.') {
		num.push_back(m_expression[pointer]);
		++pointer;
	}

	double temp = 0.0;
	std::stringstream ss;
	ss << num;
	ss >> temp;

	return new Constant(temp);
}

///--------------------------------------------------------------------

AST_Exp* Parser::parse_variable() {
	std::string temp;
	while (isalnum(m_expression[pointer])) {
		temp.push_back(m_expression[pointer]);
		++pointer;
	}

	return new Variable(temp);
}

///--------------------------------------------------------------------

AST_Exp* Parser::parse_paren() {
	++pointer;

	AST_Exp* res = parse_logic();
	if (look_ahead() == ')') {
		++pointer;
		return res;
	}
	else
		throw std::logic_error("odd number of brackets");
}

///--------------------------------------------------------------------

Kompletnie nie mam pomysłu jak takie drzewo "zacząć usuwać" z poziomu parsera przy użyciu inteligentnych wskaźników. W których w ogóle miejscach powinien występować zwykły a w których inteligentny wskaźnik... Myślę też sobie czy takie drzewo warto robić na szablonach inteligentnych wskaźników, czy to jeszcze bardziej nie komplikuje sprawy...

 

Będę wam bardzo wdzięczny za pomoc :)

komentarz zamknięcia: poblem rozwiąny

2 odpowiedzi

+1 głos
odpowiedź 16 grudnia 2018 przez j23 Mędrzec (194,920 p.)
wybrane 17 grudnia 2018 przez Jakub 0
 
Najlepsza

Kiedyś pisałem podobny parser i wszędzie używałem std::unique_ptr, tzn. wszystkie metody, takie jak parse_expression czy parse_term, zwracały inteligentny wskaźnik. Żadnych gołych wskaźników czy new i delete ;)

 

Kompletnie nie mam pomysłu jak takie drzewo "zacząć usuwać" z poziomu parsera przy użyciu inteligentnych wskaźników.

Usuwasz to, co zwraca parse_expression, reszta sama się zrobi.

komentarz 17 grudnia 2018 przez Jakub 0 Pasjonat (23,120 p.)

Dziękuje za odpowiedź, też wcześniej myślałem nad takim rozwiązaniem. Nie mogłem się tylko zdecydować czy odpowiedzialność za usuwanie drzewa ma być przekazania inteligentnym wskaźnikom w klasach reprezentujących wyrażenie czy jednak tym w parserze. Wtedy to już wszystko mi się zaczęło mylić i komplikować...

Zrobiłem tak jak mi poradziłeś tyle że zastosowałem szablon klasy std::shared_ptr bo std::unique_ptr powodował błędy kompilacji ze znanych powodów... Nie jestem jednak pewny czy to co zrobiłem działa poprawnie, będę musiał wykonać jakieś testy :).

Oto kod:

Program.h

#ifndef VALUE_H_
#define VALUE_H_

#include <string>
#include <stdexcept>
#include <map>
#include <memory>

///--------------------------------------------------------------------

using Memory = std::map<std::string, double>;

//////////////////////////// EXPRESSION //////////////////////////////////////////

class AST_Exp {
public:
	virtual double Solve(Memory& vars) = 0;
};

///--------------------------------------------------------------------

class Constant : public AST_Exp {
private:
	const double m_constant;
public:
	Constant(double constant) : m_constant(constant) {}
	virtual double Solve(Memory& vars);
};

///--------------------------------------------------------------------

class BinaryOperator : public AST_Exp {
private:
	char m_operator;
	std::shared_ptr<AST_Exp> m_left;
	std::shared_ptr<AST_Exp> m_right;
public:
	BinaryOperator(char operat, std::shared_ptr<AST_Exp> left, std::shared_ptr<AST_Exp> right)
		: m_operator(operat), m_left(left), m_right(right) {} //w tym momencie unique_ptr powodował błędy 
	virtual double Solve(Memory& vars);
};

///--------------------------------------------------------------------

class Variable : public AST_Exp {
private:
	std::string m_name;
public:
	Variable(const std::string& variable) : m_name(variable) {}
	virtual double Solve(Memory& vars);
};

//////////////////////////// PROGRAM /////////////////////////////////////////////

#endif

Parser.h

#ifndef PARSER_H_
#define PARSER_H_
#include "program.h"
#include <string>
#include <memory>

///--------------------------------------------------------------------

using AST_Exp_BOX = std::shared_ptr<AST_Exp>; 

///--------------------------------------------------------------------

class Parser {
private:
	const char EOS = 0;
	std::string m_expression;
	unsigned pointer;

private:
	void skip_whitespace();
	char look_ahead();

public:

	Parser(std::string input);
	AST_Exp_BOX parse_expression(); 
	AST_Exp_BOX parse_logic(); 
	AST_Exp_BOX parse_sum(); 
	AST_Exp_BOX parse_mult(); 
	AST_Exp_BOX parse_term(); 

	AST_Exp_BOX parse_constant();
	AST_Exp_BOX parse_variable();
	AST_Exp_BOX parse_paren();
};

///--------------------------------------------------------------------

#endif 

Parser.cpp

#include <string>
#include <cctype>
#include <stdexcept>
#include <sstream>
#include "program.h"
#include "Parser.h"

///--------------------------------------------------------------------

Parser::Parser(std::string input)
	: pointer(0), m_expression(input) {
	m_expression.push_back(EOS);
}

///--------------------------------------------------------------------

void Parser::skip_whitespace() {
	while (isspace(m_expression[pointer]))
		++pointer;
}

///--------------------------------------------------------------------

char Parser::look_ahead() {
	skip_whitespace();
	return m_expression[pointer];
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_expression() {
	AST_Exp_BOX res = parse_logic();
	if (look_ahead() == EOS)
		return res;
	else
		throw std::logic_error("bad code structure");
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_logic() {
	AST_Exp_BOX res = parse_sum();
	char c = look_ahead();
	while (c == '=' || c == '>' || c == '<' || c == '!') {
		++pointer;
		res = AST_Exp_BOX(new BinaryOperator(c, res, parse_sum()));
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_sum() {
	AST_Exp_BOX res = parse_mult();
	char c = look_ahead();
	while (c == '+' || c == '-') {
		++pointer;
		res = AST_Exp_BOX(new BinaryOperator(c, res, parse_mult()));
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_mult() {
	AST_Exp_BOX res = parse_term();
	char c = look_ahead();
	while (c == '*' || c == '/' || c == '%' || c == '^') {
		++pointer;
		res = AST_Exp_BOX(new BinaryOperator(c, res, parse_term()));
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_term() {
	char c = look_ahead();
	if (std::isdigit(c))
		return parse_constant();
	else if (std::isalpha(c))
		return parse_variable();
	else if (c == '(')
		return parse_paren();
	else
		throw std::logic_error("undefined operator");
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_constant() {
	std::string num;
	while (std::isdigit(m_expression[pointer]) || m_expression[pointer] == '.') {
		num.push_back(m_expression[pointer]);
		++pointer;
	}

	double temp = 0.0;
	std::stringstream ss;
	ss << num;
	ss >> temp;

	return AST_Exp_BOX(new Constant(temp));
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_variable() {
	std::string temp;
	while (isalnum(m_expression[pointer])) {
		temp.push_back(m_expression[pointer]);
		++pointer;
	}

	return AST_Exp_BOX(new Variable(temp));
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_paren() {
	++pointer;

	AST_Exp_BOX res = parse_logic();
	if (look_ahead() == ')') {
		++pointer;
		return res;
	}
	else
		throw std::logic_error("odd number of brackets");
}

///--------------------------------------------------------------------

Udostępniłem kod bo może zawiera on jakieś łatwe do zauważenia fatalne błędy, na które ja nie zwróciłem uwagi...

1
komentarz 17 grudnia 2018 przez j23 Mędrzec (194,920 p.)

bo std::unique_ptr powodował błędy kompilacji ze znanych powodów...

Pewnie chodziło o przypisanie — std::unique_ptr nie ma operatora przypisania, wskaźnik może być jedynie przenoszony, dlatego czasem trzeba użyć std::move.

 

Drobna uwaga odnośnie do std::shared_ptr: jeśli tworzysz obiekty dla tego wskaźnika, użyj std::make_shared. Użycie tej funkcji ograniczy liczbę alokacji do jednej (teraz masz dwie — jedna dla licznika referencji, druga dla obiektu).

 

Dlaczego funkcje parse_* są publiczne?

komentarz 17 grudnia 2018 przez Jakub 0 Pasjonat (23,120 p.)

Dlaczego funkcje parse_* są publiczne?

Faktycznie, niepotrzebnie są publiczne. 

Rozumiem że std::make_shared stosujemy w miejscu new? :

#include <string>
#include <cctype>
#include <stdexcept>
#include <sstream>
#include "program.h"
#include "Parser.h"

///--------------------------------------------------------------------

Parser::Parser(std::string input)
	: pointer(0), m_expression(input) {
	m_expression.push_back(EOS);
}

///--------------------------------------------------------------------

void Parser::skip_whitespace() {
	while (isspace(m_expression[pointer]))
		++pointer;
}

///--------------------------------------------------------------------

char Parser::look_ahead() {
	skip_whitespace();
	return m_expression[pointer];
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_expression() {
	AST_Exp_BOX res = parse_logic();
	if (look_ahead() == EOS)
		return res;
	else
		throw std::logic_error("bad code structure");
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_logic() {
	AST_Exp_BOX res = parse_sum();
	char c = look_ahead();
	while (c == '=' || c == '>' || c == '<' || c == '!') {
		++pointer;
		res = std::make_shared<AST_Exp>(BinaryOperator(c, res, parse_sum()));
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_sum() {
	AST_Exp_BOX res = parse_mult();
	char c = look_ahead();
	while (c == '+' || c == '-') {
		++pointer;
		res = std::make_shared<AST_Exp>(BinaryOperator(c, res, parse_mult()));
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_mult() {
	AST_Exp_BOX res = parse_term();
	char c = look_ahead();
	while (c == '*' || c == '/' || c == '%' || c == '^') {
		++pointer;
		res = std::make_shared<AST_Exp>(BinaryOperator(c, res, parse_term()));
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_term() {
	char c = look_ahead();
	if (std::isdigit(c))
		return parse_constant();
	else if (std::isalpha(c))
		return parse_variable();
	else if (c == '(')
		return parse_paren();
	else
		throw std::logic_error("undefined operator");
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_constant() {
	std::string num;
	while (std::isdigit(m_expression[pointer]) || m_expression[pointer] == '.') {
		num.push_back(m_expression[pointer]);
		++pointer;
	}

	double temp = 0.0;
	std::stringstream ss;
	ss << num;
	ss >> temp;

	AST_Exp_BOX res;
	res = std::make_shared<AST_Exp>(Constant(temp));
	return res;
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_variable() {
	std::string temp;
	while (isalnum(m_expression[pointer])) {
		temp.push_back(m_expression[pointer]);
		++pointer;
	}

	AST_Exp_BOX res;
	res = std::make_shared<AST_Exp>(Variable(temp));
	return res;
}

///--------------------------------------------------------------------

AST_Exp_BOX Parser::parse_paren() {
	++pointer;

	AST_Exp_BOX res = parse_logic();
	if (look_ahead() == ')') {
		++pointer;
		return res;
	}
	else
		throw std::logic_error("odd number of brackets");
}

///--------------------------------------------------------------------

Mam jednak teraz błąd że próbuję stworzyć instancję klasy abstrakcyjnej, no ale przecież mając wskaźnik typu klasy bazowej możemy nim wskazać na obiekty klas pochodnych...

 

1
komentarz 17 grudnia 2018 przez j23 Mędrzec (194,920 p.)

Spróbuj tak:

res = std::make_shared<BinaryOperator>(c, res, parse_term());

 

I dodaj wirtualny destruktor do klasy bazowej.

komentarz 17 grudnia 2018 przez Jakub 0 Pasjonat (23,120 p.)
Teraz działa, dziękuje bardzo za pomoc.

Jeszcze nie zamykam pytania bo zastanawiam się czy jednak nie dam rady tego zrobić na std::unique_ptr używając move... Ale to już jutro :) Muszę jeszcze raz przeanalizować sobie całe drzewo.
komentarz 19 grudnia 2018 przez Jakub 0 Pasjonat (23,120 p.)

Witam ponownie :)

Po kilku próbach, po nerwach oraz frustracjach wreszcie udało mi się to rozwiązać z użyciem std::unique_ptr, wydawało mi się wcześniej że znam inteligentne wskaźniki, bo o nich czytałem w książce, jednak wykorzystanie tego w praktyce po raz pierwszy było dla mnie już dużym wyzwaniem...

Najważniejsze części kodu prezentują się teraz tak:
 

Program.h

 

#ifndef VALUE_H_
#define VALUE_H_

#include <string>
#include <stdexcept>
#include <map>
#include <memory>

///--------------------------------------------------------------------

using Memory = std::map<std::string, double>;

//////////////////////////// EXPRESSION //////////////////////////////////////////

class AST_Exp {
public:
	virtual double Solve(Memory& vars) = 0;
	virtual ~AST_Exp() {}
};

///--------------------------------------------------------------------

class Constant : public AST_Exp {
private:
	const double m_constant;
public:
	Constant(double constant) : m_constant(constant) {}
	virtual double Solve(Memory& vars);
};

///--------------------------------------------------------------------

class BinaryOperator : public AST_Exp {
private:
	char m_operator;
	std::unique_ptr<AST_Exp> m_left;
	std::unique_ptr<AST_Exp> m_right;
public:
	BinaryOperator(char operat, std::unique_ptr<AST_Exp> left, std::unique_ptr<AST_Exp> right)
		: m_operator(operat) {
		m_left = std::move(left);
		m_right = std::move(right);
	}
	virtual double Solve(Memory& vars);
};

///--------------------------------------------------------------------

class Variable : public AST_Exp {
private:
	std::string m_name;
public:
	Variable(const std::string& variable) : m_name(variable) {}
	virtual double Solve(Memory& vars);
};

//////////////////////////// PROGRAM /////////////////////////////////////////////

#endif





Parser.h

#ifndef PARSER_H_
#define PARSER_H_

#include "program.h"
#include <string>

///--------------------------------------------------------------------

class Parser {
private:
	const char EOS = 0;
	std::string m_expression;
	unsigned pointer;

private:
	void skip_whitespace();
	char look_ahead();

public:
	Parser(std::string input);
	std::unique_ptr<AST_Exp> parse_expression();

private:

	std::unique_ptr<AST_Exp> parse_logic();
	std::unique_ptr<AST_Exp> parse_sum();
	std::unique_ptr<AST_Exp> parse_mult();
	std::unique_ptr<AST_Exp> parse_term();
						   
	std::unique_ptr<AST_Exp> parse_constant();
	std::unique_ptr<AST_Exp> parse_variable();
	std::unique_ptr<AST_Exp> parse_paren();
};

///--------------------------------------------------------------------

#endif 






 

Parser.cpp

#include <string>
#include <cctype>
#include <stdexcept>
#include <sstream>
#include "program.h"
#include "Parser.h"
#include <memory>

///--------------------------------------------------------------------

Parser::Parser(std::string input)
	: pointer(0), m_expression(input) {
	m_expression.push_back(EOS);
}

///--------------------------------------------------------------------

void Parser::skip_whitespace() {
	while (isspace(m_expression[pointer]))
		++pointer;
}

///--------------------------------------------------------------------

char Parser::look_ahead() {
	skip_whitespace();
	return m_expression[pointer];
}

///--------------------------------------------------------------------

std::unique_ptr<AST_Exp> Parser::parse_expression() {
	auto res = parse_logic();
	if (look_ahead() == EOS)
		return res;
	else
		throw std::logic_error("bad code structure");
}

///--------------------------------------------------------------------

std::unique_ptr<AST_Exp> Parser::parse_logic() {
	auto  res = parse_sum();
	char c = look_ahead();
	while (c == '=' || c == '>' || c == '<' || c == '!') {
		++pointer;
		res = std::make_unique<BinaryOperator>(c, std::move(res), parse_sum());
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

std::unique_ptr<AST_Exp> Parser::parse_sum() {
	auto res = parse_mult();
	char c = look_ahead();
	while (c == '+' || c == '-') {
		++pointer;
		res = std::make_unique<BinaryOperator>(c, std::move(res), parse_mult());
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

std::unique_ptr<AST_Exp> Parser::parse_mult() {
	auto res = parse_term();
	char c = look_ahead();
	while (c == '*' || c == '/' || c == '%' || c == '^') {
		++pointer;
		res = std::make_unique<BinaryOperator>(c, std::move(res), parse_term());
		c = look_ahead();
	}
	return res;
}

///--------------------------------------------------------------------

std::unique_ptr<AST_Exp> Parser::parse_term() {
	char c = look_ahead();
	if (std::isdigit(c))
		return parse_constant();
	else if (std::isalpha(c))
		return parse_variable();
	else if (c == '(')
		return parse_paren();
	else
		throw std::logic_error("undefined character");
}

///--------------------------------------------------------------------

std::unique_ptr<AST_Exp> Parser::parse_constant() {
	std::string num;
	while (std::isdigit(m_expression[pointer]) || m_expression[pointer] == '.') {
		num.push_back(m_expression[pointer]);
		++pointer;
	}

	double temp = 0.0;
	std::stringstream ss;
	ss << num;
	ss >> temp;

	return std::make_unique<Constant>(temp);
}

///--------------------------------------------------------------------

std::unique_ptr<AST_Exp> Parser::parse_variable() {
	std::string temp;
	while (isalnum(m_expression[pointer])) {
		temp.push_back(m_expression[pointer]);
		++pointer;
	}

	return std::make_unique<Variable>(temp);
}

///--------------------------------------------------------------------

std::unique_ptr<AST_Exp> Parser::parse_paren() {
	++pointer;

	auto res = parse_logic();
	if (look_ahead() == ')') {
		++pointer;
		return res;
	}
	else
		throw std::logic_error("odd number of brackets");
}

///--------------------------------------------------------------------

 

komentarz 19 grudnia 2018 przez j23 Mędrzec (194,920 p.)
edycja 19 grudnia 2018 przez j23

Nie prościej dać gdzieś na początku:

using AST_ExpPtr = std::unique_ptr<AST_Exp>;

i zamienić wiązankę std::unique_ptr<AST_Exp> na alias, by kod był bardziej czytelny?

W linii 12 (Parser.cpp) daj m_expression(std::move(input)).

 

PS. dodaj obsługę funkcji, to znacznie rozszerzy funkcjonalność twojego parsera ;)

komentarz 19 grudnia 2018 przez Jakub 0 Pasjonat (23,120 p.)

Nie prościej dać gdzieś na początku:

using AST_ExpPtr = std::unique_ptr<AST_Exp>;

 

i zamienić wiązankę std::unique_ptr<AST_Exp> na alias, by kod był bardziej czytelny?

Tak miałem wcześniej i tu też tak oczywiście zrobię.

W linii 12 (Parser.cpp) daj m_expression(std::move(input)).

Faktem jest to, że parametr typu string o nazwie input powinien być najlepiej stałą referencją... nie rozumiem jednak czemu w tym przypadku używać std::move() ? Przecież to nawet nie jest wskaźnik :/.

PS. dodaj obsługę funkcji, to znacznie rozszerzy funkcjonalność twojego parsera ;)

To oczywiście nie jest gotowy parser :) To jedynie jego elementy przetwarzające wyrażenie ( np. kiedy coś przypisujemy do zmiennej, lub jako warunek pętli... ). Potem dodam złożone instrukcje, na razie tylko chciałem się uporać z bezpiecznym usuwaniem drzewa. A same funkcje to już dosyć złożona rzecz do dodania na samym końcu.

* W sumie jednak nie, bo funkcje zawierają w sobie wszelkie inne instrukcje... To by wymagało ode mnie sporych chyba sporych zmian w budowie parsera.

 

1
komentarz 19 grudnia 2018 przez j23 Mędrzec (194,920 p.)

Faktem jest to, że parametr typu string o nazwie input powinien być najlepiej stałą referencją

Wbrew pozorom lepiej jest w tym przypadku przekazać przez wartość. Policz sobie (dla obu przypadków), ile będzie kopiowania i alokacji, jak w argumencie dasz np. literał tekstowy.

nie rozumiem jednak czemu w tym przypadku używać std::move() ?

Po to, by nie kopiować niepotrzebnie obiektu input.

A same funkcje to już dosyć złożona rzecz do dodania na samym końcu.

Parsowanie wywołania funkcji to banał. Z drugiej strony, jeśli piszesz coś na wzór języka skryptowego, powinieneś zacząć od tokenizera, żeby nie babrać się z przetwarzaniem tekstu w parserze.

 

+1 głos
odpowiedź 16 grudnia 2018 przez mokrowski Mędrzec (155,460 p.)
Użyj std::unique_ptr. Jedyny problem jaki może się pojawić to destrukcja dużego pod-drzewa. W przypadku takich drzew, należy przejść iteracją po elementach i je niszczyć. Duża ilość destrukcji może po prostu przeciążyć stos. Z doświadczenia ~1500 elementów (to może być różna wartość w zależności od OS i sposobu alokowania)

Jeśli będziesz chciał mieć wskaźnik do części drzewa (wymaganie aplikacji), wtedy decydował będziesz się na std::shared_ptr aby w trakcie jego usunięcia nie dopuścić do zerwania wskaźnika na używane gdzieś indziej pod-drzewo.

Podobne pytania

0 głosów
2 odpowiedzi 162 wizyt
pytanie zadane 8 lipca 2018 w C i C++ przez biesak Nowicjusz (120 p.)
–9 głosów
3 odpowiedzi 6,173 wizyt
pytanie zadane 29 stycznia 2018 w Sprzęt komputerowy przez Rumpel Nowicjusz (120 p.)

92,537 zapytań

141,377 odpowiedzi

319,456 komentarzy

61,922 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...