From af9e0b57dac45dc5591f16fb34236b1356cda8a2 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Sun, 26 Aug 2012 20:46:06 +0200 Subject: [PATCH] added basic parser (not completely tested) --- Debug/src/loader/subdir.mk | 6 + Release/src/loader/subdir.mk | 6 + src/loader/ParsedSource.cpp | 170 ++++++++++++++++++++ src/loader/ParsedSource.h | 157 ++++++++++++++++++ src/loader/Parser.cpp | 297 +++++++++++++++++++++++++++++++++++ src/loader/Parser.h | 75 +++++++++ src/loader/Tokenizer.cpp | 2 +- src/main.cpp | 14 ++ 8 files changed, 726 insertions(+), 1 deletion(-) create mode 100644 src/loader/ParsedSource.cpp create mode 100644 src/loader/ParsedSource.h create mode 100644 src/loader/Parser.cpp create mode 100644 src/loader/Parser.h diff --git a/Debug/src/loader/subdir.mk b/Debug/src/loader/subdir.mk index e91a415..94e1495 100644 --- a/Debug/src/loader/subdir.mk +++ b/Debug/src/loader/subdir.mk @@ -4,12 +4,18 @@ # Add inputs and outputs from these tool invocations to the build variables CPP_SRCS += \ +../src/loader/ParsedSource.cpp \ +../src/loader/Parser.cpp \ ../src/loader/Tokenizer.cpp OBJS += \ +./src/loader/ParsedSource.o \ +./src/loader/Parser.o \ ./src/loader/Tokenizer.o CPP_DEPS += \ +./src/loader/ParsedSource.d \ +./src/loader/Parser.d \ ./src/loader/Tokenizer.d diff --git a/Release/src/loader/subdir.mk b/Release/src/loader/subdir.mk index 8c6e88c..c865204 100644 --- a/Release/src/loader/subdir.mk +++ b/Release/src/loader/subdir.mk @@ -4,12 +4,18 @@ # Add inputs and outputs from these tool invocations to the build variables CPP_SRCS += \ +../src/loader/ParsedSource.cpp \ +../src/loader/Parser.cpp \ ../src/loader/Tokenizer.cpp OBJS += \ +./src/loader/ParsedSource.o \ +./src/loader/Parser.o \ ./src/loader/Tokenizer.o CPP_DEPS += \ +./src/loader/ParsedSource.d \ +./src/loader/Parser.d \ ./src/loader/Tokenizer.d diff --git a/src/loader/ParsedSource.cpp b/src/loader/ParsedSource.cpp new file mode 100644 index 0000000..75d7efb --- /dev/null +++ b/src/loader/ParsedSource.cpp @@ -0,0 +1,170 @@ +/* + * ParsedSource.cpp + * + * Created on: Aug 26, 2012 + * Author: holy + */ + +#include "ParsedSource.h" + +#include +#include + +using std::map; +using std::runtime_error; +using std::string; +using std::vector; + +namespace loader { + +void ParsedSource::AddDeclaration(Declaration *d) { + map::iterator i(declarations.find(d->Identifier())); + if (i != declarations.end()) { + if (d->TypeName() != i->second->TypeName()) { + throw runtime_error("invalid redeclaration of " + i->second->TypeName() + " " + d->Identifier()); + } + } else { + declarations.insert(std::make_pair(d->Identifier(), d)); + } +} + +void ParsedSource::ExportDeclaration(Declaration *d) { + AddDeclaration(d); + exports.insert(d->Identifier()); +} + +void ParsedSource::ExportIdentifier(const std::string &i) { + if (declarations.count(i)) { + exports.insert(i); + } else { + throw runtime_error("cannot export undeclared identifier " + i); + } +} + + +void Definition::SetValue(Literal *v) { + value = v; + isLiteral = true; +} + +void Definition::SetValue(PropertyList *v) { + value = v; + isLiteral = false; +} + +Literal *Definition::GetLiteral() { + if (isLiteral) { + return (Literal *)value; + } else { + throw runtime_error("tried to access properties as literal"); + } +} + +const Literal *Definition::GetLiteral() const { + if (isLiteral) { + return (Literal *)value; + } else { + throw runtime_error("tried to access properties as literal"); + } +} + +PropertyList *Definition::GetProperties() { + if (!isLiteral) { + return (PropertyList *)value; + } else { + throw runtime_error("tried to access literal value as property list"); + } +} + +const PropertyList *Definition::GetProperties() const { + if (!isLiteral) { + return (PropertyList *)value; + } else { + throw runtime_error("tried to access literal value as property list"); + } +} + + +PropertyList::~PropertyList() { + for (map::iterator i(props.begin()), end(props.end()); i != end; ++i) { + delete i->second; + } +} + + +Literal::Literal(const vector &v) +: props(0) +, values(v) +, i1(0), i2(0) +, i3(0), i4(0) +, b(false) +, type(ARRAY) { + +} + +Literal::Literal(bool b) +: props(0) +, i1(0), i2(0) +, i3(0), i4(0) +, b(b) +, type(BOOLEAN) { + +} + +Literal::Literal(int r, int g, int b, int a) +: props(0) +, i1(r), i2(g) +, i3(b), i4(a) +, b(false) +, type(COLOR) { + +} + +Literal::Literal(const string &str) +: props(0) +, str(str) +, i1(0), i2(0) +, i3(0), i4(0) +, b(false) +, type(STRING) { + +} + +Literal::Literal(int x, int y) +: props(0) +, i1(x), i2(y) +, i3(0), i4(0) +, b(false) +, type(VECTOR) { + +} + +Literal::Literal(const string &typeName, PropertyList *properties) +: props(properties) +, str(typeName) +, i1(0), i2(0) +, i3(0), i4(0) +, b(false) +, type(OBJECT) { + +} + +} + + +namespace std { + +ostream &operator <<(ostream &out, const loader::ParsedSource &source) { + out << "parsed source file" << endl; + out << "declared objects: " << endl; + for (map::const_iterator i(source.Declarations().begin()), end(source.Declarations().end()); i != end; ++i) { + out << " - " << i->first << " of type " << i->second->TypeName() << endl; + } + out << "exported objects: " << endl; + for (set::const_iterator i(source.Exports().begin()), end(source.Exports().end()); i != end; ++i) { + out << " - " << *i << endl; + } + return out; +} + +} diff --git a/src/loader/ParsedSource.h b/src/loader/ParsedSource.h new file mode 100644 index 0000000..ebcc853 --- /dev/null +++ b/src/loader/ParsedSource.h @@ -0,0 +1,157 @@ +/* + * ParsedSource.h + * + * Created on: Aug 26, 2012 + * Author: holy + */ + +#ifndef LOADER_PARSEDSOURCE_H_ +#define LOADER_PARSEDSOURCE_H_ + +#include +#include +#include +#include +#include + +namespace loader { + +class PropertyList; +class Value; + +class Literal { + + enum Type { + ARRAY, + BOOLEAN, + COLOR, + NUMBER, + STRING, + VECTOR, + OBJECT + }; + +public: + explicit Literal(const std::vector &); + explicit Literal(bool); + Literal(int r, int g, int b, int a = 255); + Literal(const std::string &); + Literal(int x, int y); + Literal(const std::string &typeName, PropertyList *properties); + +private: + PropertyList *props; + std::string str; + std::vector values; + int i1, i2, i3, i4; + bool b; + Type type; + +}; + + +class Value { + +public: + explicit Value(const std::string &identifier) + : literal(0), identifier(identifier), isLiteral(false) { } + explicit Value(Literal *literal) + : literal(literal), isLiteral(false) { } + +private: + Literal *literal; + std::string identifier; + bool isLiteral; + +}; + + +class PropertyList { + +public: + ~PropertyList(); + +public: + void SetProperty(const std::string &name, Value *value) { + props[name] = value; + } + +private: + std::map props; + +}; + + +class Declaration { + +public: + Declaration(const std::string &typeName, const std::string &identifier) + : typeName(typeName), identifier(identifier) { } + virtual ~Declaration() { } + +public: + const std::string &TypeName() const { return typeName; } + const std::string &Identifier() const { return identifier; } + +private: + std::string typeName; + std::string identifier; + +}; + + +class Definition +: public Declaration { + +public: + Definition(const std::string &typeName, const std::string &identifier) + : Declaration(typeName, identifier), value(0), isLiteral(false) { } + +public: + void SetValue(Literal *); + void SetValue(PropertyList *); + + bool HasLiteralValue() const { return isLiteral && value; } + bool HasProperties() const { return !isLiteral && value; } + Literal *GetLiteral(); + const Literal *GetLiteral() const; + PropertyList *GetProperties(); + const PropertyList *GetProperties() const; + +private: + void *value; + bool isLiteral; + +}; + + +class ParsedSource { + +public: + ParsedSource() { } + ~ParsedSource() { } + +public: + void AddDeclaration(Declaration *); + void ExportDeclaration(Declaration *); + void ExportIdentifier(const std::string &); + + const std::map Declarations() const { return declarations; } + const std::set Exports() const { return exports; } + +private: + std::map declarations; + std::set exports; + +}; + +} + + +namespace std { + +ostream &operator <<(ostream &, const loader::ParsedSource &); + +} + +#endif /* LOADER_PARSEDSOURCE_H_ */ diff --git a/src/loader/Parser.cpp b/src/loader/Parser.cpp new file mode 100644 index 0000000..2cf1a34 --- /dev/null +++ b/src/loader/Parser.cpp @@ -0,0 +1,297 @@ +/* + * Parser.cpp + * + * Created on: Aug 26, 2012 + * Author: holy + */ + +#include "Parser.h" + +#include +#include + +using std::auto_ptr; +using std::ifstream; +using std::string; +using std::vector; + +namespace loader { + +void Parser::Parse() { + while (tok.HasMore()) { + ParseStatement(); + } +} + +void Parser::ParseStatement() { + Tokenizer::Token t(tok.GetNext()); + switch (t.type) { + case Tokenizer::Token::KEYWORD_EXPORT: + ParseExportDirective(); + break; + case Tokenizer::Token::KEYWORD_INCLUDE: + ParseIncludeDirective(); + break; + case Tokenizer::Token::TYPE_NAME: + tok.Putback(t); + { + Declaration *decl(ProbeDefinition()); + product.AddDeclaration(decl); + } + break; + default: + throw ParseError(string("unexpected token ") + TokenTypeToString(t.type)); + } +} + +void Parser::ParseExportDirective() { + Tokenizer::Token t(tok.GetNext()); + if (t.type != Tokenizer::Token::IDENTIFIER) { + tok.Putback(t); + Declaration *decl(ProbeDefinition()); + product.ExportDeclaration(decl); + } else { + product.ExportIdentifier(t.str); + } +} + +void Parser::ParseIncludeDirective() { + Tokenizer::Token t(tok.GetNext()); + AssertTokenType(t.type, Tokenizer::Token::STRING); + ifstream file(t.str.c_str()); // TODO: resolve path name + Parser sub(file, product); + sub.Parse(); +} + +Declaration *Parser::ProbeDefinition() { + string typeName(ParseTypeName()); + string identifier(ParseIdentifier()); + + Tokenizer::Token t(tok.GetNext()); + tok.Putback(t); + if (BeginOfPropertyList(t)) { + PropertyList *propertyList(ParsePropertyList()); + Definition *dfn(new Definition(typeName, identifier)); + dfn->SetValue(propertyList); + return dfn; + } else if (BeginningOfLiteral(t)) { + Literal *literal(ParseLiteral()); + Definition *dfn(new Definition(typeName, identifier)); + dfn->SetValue(literal); + return dfn; + } else { + return new Declaration(typeName, identifier); + } +} + +bool Parser::BeginningOfLiteral(const Tokenizer::Token &t) const { + switch (t.type) { + case Tokenizer::Token::CHEVRON_OPEN: + case Tokenizer::Token::BRACKET_OPEN: + case Tokenizer::Token::PARENTHESIS_OPEN: + case Tokenizer::Token::NUMBER: + case Tokenizer::Token::STRING: + case Tokenizer::Token::KEYWORD_FALSE: + case Tokenizer::Token::KEYWORD_TRUE: + case Tokenizer::Token::TYPE_NAME: + return true; + default: + return false; + } +} + +bool Parser::BeginOfPropertyList(const Tokenizer::Token &t) const { + return t.type == Tokenizer::Token::ANGLE_BRACKET_OPEN; +} + +Definition *Parser::ParseDefinition() { + string typeName(ParseTypeName()); + string identifier(ParseIdentifier()); + + Tokenizer::Token t(tok.GetNext()); + tok.Putback(t); + if (BeginOfPropertyList(t)) { + PropertyList *propertyList(ParsePropertyList()); + Definition *dfn(new Definition(typeName, identifier)); + dfn->SetValue(propertyList); + return dfn; + } else if (BeginningOfLiteral(t)) { + Literal *literal(ParseLiteral()); + Definition *dfn(new Definition(typeName, identifier)); + dfn->SetValue(literal); + return dfn; + } else { + throw ParseError(string("unexpected token ") + TokenTypeToString(t.type) + ", expected property-list or literal"); + } +} + +string Parser::ParseIdentifier() { + Tokenizer::Token t(tok.GetNext()); + AssertTokenType(t.type, Tokenizer::Token::IDENTIFIER); + return t.str; +} + +string Parser::ParseTypeName() { + Tokenizer::Token t(tok.GetNext()); + AssertTokenType(t.type, Tokenizer::Token::TYPE_NAME); + return t.str; +} + +PropertyList *Parser::ParsePropertyList() { + Tokenizer::Token t(tok.GetNext()); + AssertTokenType(t.type, Tokenizer::Token::ANGLE_BRACKET_OPEN); + + auto_ptr props(new PropertyList); + + while (t.type != Tokenizer::Token::ANGLE_BRACKET_CLOSE) { + Tokenizer::Token name(tok.GetNext()); + AssertTokenType(name.type, Tokenizer::Token::IDENTIFIER); + + t = tok.GetNext(); + AssertTokenType(t.type, Tokenizer::Token::COLON); + + Value *value(ParseValue()); + props->SetProperty(name.str, value); + + t = tok.GetNext(); + if (t.type != Tokenizer::Token::ANGLE_BRACKET_CLOSE && t.type != Tokenizer::Token::COMMA) { + throw ParseError(string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or }"); + } + } + + return props.release(); +} + +Value *Parser::ParseValue() { + Tokenizer::Token t(tok.GetNext()); + if (t.type == Tokenizer::Token::IDENTIFIER) { + return new Value(t.str); + } else if (BeginningOfLiteral(t)) { + tok.Putback(t); + Literal *literal(ParseLiteral()); + return new Value(literal); + } else { + throw new ParseError(string("unexpected token ") + TokenTypeToString(t.type) + ", expected literal or identifier"); + } +} + +Literal *Parser::ParseLiteral() { + Tokenizer::Token t(tok.GetNext()); + if (t.type == Tokenizer::Token::TYPE_NAME) { + PropertyList *props(ParsePropertyList()); + return new Literal(t.str, props); + } else if (BeginningOfLiteral(t)) { + switch (t.type) { + case Tokenizer::Token::CHEVRON_OPEN: + tok.Putback(t); + return ParseVector(); + case Tokenizer::Token::BRACKET_OPEN: + tok.Putback(t); + return ParseArray(); + case Tokenizer::Token::PARENTHESIS_OPEN: + tok.Putback(t); + return ParseColor(); + case Tokenizer::Token::NUMBER: + return new Literal(t.number); + case Tokenizer::Token::STRING: + return new Literal(t.str); + case Tokenizer::Token::KEYWORD_FALSE: + return new Literal(false); + case Tokenizer::Token::KEYWORD_TRUE: + return new Literal(true); + default: + throw std::logic_error("literal switch reached impossible default branch oO"); + } + } else { + throw new ParseError(string("unexpected token ") + TokenTypeToString(t.type) + ", expected type-name or primitive"); + } +} + +Literal *Parser::ParseArray() { + Tokenizer::Token t(tok.GetNext()); + AssertTokenType(t.type, Tokenizer::Token::BRACKET_OPEN); + + vector values; + + while (t.type != Tokenizer::Token::ANGLE_BRACKET_CLOSE) { + Value *value(ParseValue()); + values.push_back(value); + + t = tok.GetNext(); + if (t.type != Tokenizer::Token::BRACKET_CLOSE && t.type != Tokenizer::Token::COMMA) { + throw ParseError(string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or ]"); + } + } + + return new Literal(values); +} + +Literal *Parser::ParseColor() { + string msg("error parsing color"); + Tokenizer::Token t(tok.GetNext()); + AssertTokenType(t.type, Tokenizer::Token::PARENTHESIS_OPEN, msg); + + Tokenizer::Token red(tok.GetNext()); + AssertTokenType(red.type, Tokenizer::Token::NUMBER, "error parsing red component of color"); + + t = tok.GetNext(); + AssertTokenType(t.type, Tokenizer::Token::COMMA, msg); + + Tokenizer::Token green(tok.GetNext()); + AssertTokenType(green.type, Tokenizer::Token::NUMBER, "error parsing green component of color"); + + t = tok.GetNext(); + AssertTokenType(t.type, Tokenizer::Token::COMMA, msg); + + Tokenizer::Token blue(tok.GetNext()); + AssertTokenType(blue.type, Tokenizer::Token::NUMBER, "error parsing blue component of color"); + + t = tok.GetNext(); + if (t.type == Tokenizer::Token::BRACKET_CLOSE) { + return new Literal(red.number, green.number, blue.number); + } else if (t.type != Tokenizer::Token::COMMA) { + Tokenizer::Token alpha(tok.GetNext()); + AssertTokenType(alpha.type, Tokenizer::Token::NUMBER, "error parsing alpha component of color"); + + t = tok.GetNext(); + AssertTokenType(t.type, Tokenizer::Token::PARENTHESIS_CLOSE, msg); + + return new Literal(red.number, green.number, blue.number, alpha.number); + } else { + throw ParseError(string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or ]"); + } +} + +Literal *Parser::ParseVector() { + std::string msg("error parsing vector"); + Tokenizer::Token t(tok.GetNext()); + AssertTokenType(t.type, Tokenizer::Token::CHEVRON_OPEN, msg); + + Tokenizer::Token x(tok.GetNext()); + AssertTokenType(x.type, Tokenizer::Token::NUMBER, "error parsing x component of vector"); + + t = tok.GetNext(); + AssertTokenType(t.type, Tokenizer::Token::COMMA, msg); + + Tokenizer::Token y(tok.GetNext()); + AssertTokenType(y.type, Tokenizer::Token::NUMBER, "error parsing y component of vector"); + + t = tok.GetNext(); + AssertTokenType(t.type, Tokenizer::Token::CHEVRON_CLOSE, msg); + + return new Literal(x.number, y.number); +} + +void Parser::AssertTokenType(Tokenizer::Token::Type actual, Tokenizer::Token::Type expected) { + if (expected != actual) { + throw ParseError(string("unexpected token ") + TokenTypeToString(actual) + ", expected " + TokenTypeToString(expected)); + } +} + +void Parser::AssertTokenType(Tokenizer::Token::Type actual, Tokenizer::Token::Type expected, const string &msg) { + if (expected != actual) { + throw ParseError(msg + ": unexpected token " + TokenTypeToString(actual) + ", expected " + TokenTypeToString(expected)); + } +} + +} diff --git a/src/loader/Parser.h b/src/loader/Parser.h new file mode 100644 index 0000000..923a424 --- /dev/null +++ b/src/loader/Parser.h @@ -0,0 +1,75 @@ +/* + * Parser.h + * + * Created on: Aug 26, 2012 + * Author: holy + */ + +#ifndef LOADER_PARSER_H_ +#define LOADER_PARSER_H_ + +#include "ParsedSource.h" +#include "Tokenizer.h" + +#include +#include +#include + +namespace loader { + +class Declaration; +class Definition; +class Literal; +class PropertyList; + +class Parser { + +public: + Parser(std::istream &in, ParsedSource &product) : tok(in), product(product) { } + ~Parser() { } +private: + Parser(const Parser &); + Parser &operator =(const Parser &); + +public: + void Parse(); + +public: + class ParseError: public std::runtime_error { + public: + explicit ParseError(const std::string &msg) : std::runtime_error(msg) { }; + }; + +private: + void ParseStatement(); + void ParseExportDirective(); + void ParseIncludeDirective(); + + Declaration *ProbeDefinition(); + Definition *ParseDefinition(); + + std::string ParseIdentifier(); + std::string ParseTypeName(); + + Value *ParseValue(); + PropertyList *ParsePropertyList(); + Literal *ParseLiteral(); + Literal *ParseArray(); + Literal *ParseColor(); + Literal *ParseVector(); + +private: + void AssertTokenType(Tokenizer::Token::Type actual, Tokenizer::Token::Type expected); + void AssertTokenType(Tokenizer::Token::Type actual, Tokenizer::Token::Type expected, const std::string &msg); + bool BeginningOfLiteral(const Tokenizer::Token &) const; + bool BeginOfPropertyList(const Tokenizer::Token &) const; + +private: + Tokenizer tok; + ParsedSource &product; + +}; + +} + +#endif /* LOADER_PARSER_H_ */ diff --git a/src/loader/Tokenizer.cpp b/src/loader/Tokenizer.cpp index c5cc561..cdabe01 100644 --- a/src/loader/Tokenizer.cpp +++ b/src/loader/Tokenizer.cpp @@ -12,7 +12,7 @@ namespace loader { bool Tokenizer::HasMore() { - return in; + return std::istream::sentry(in); } void Tokenizer::Putback(const Token &t) { diff --git a/src/main.cpp b/src/main.cpp index 3dc37d8..424404f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,8 @@ #include "graphics/Menu.h" #include "graphics/SimpleAnimation.h" #include "graphics/Sprite.h" +#include "loader/ParsedSource.h" +#include "loader/Parser.h" #include "sdl/InitImage.h" #include "sdl/InitScreen.h" #include "sdl/InitSDL.h" @@ -32,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +58,8 @@ using graphics::Gauge; using graphics::Menu; using graphics::SimpleAnimation; using graphics::Sprite; +using loader::ParsedSource; +using loader::Parser; using sdl::InitImage; using sdl::InitScreen; using sdl::InitSDL; @@ -73,6 +78,15 @@ int main(int argc, char **argv) { // std::srand(std::time(0)); try { + std::ifstream file("test-data/test.l2s"); + ParsedSource source; + Parser parser(file, source); + parser.Parse(); + + cout << source; + + return 0; + InitSDL sdl; InitImage image(IMG_INIT_PNG); InitScreen screen(width, height); -- 2.39.2