Skip to content

Commit ce86d59

Browse files
authored
Implemetation of 'include' and 'import' statements (#108)
* Add render-time error handling * Add parser for 'include' statement * Small refactor of error reporting * Fix parser, add tests for 'include' statement * Fully implement 'include' statement * Add statement AST visitor * Add parser and tests for 'import'/'from' statements * Implement 'import' statement * Add extra tests for 'import' syntax * Add tests for the parsing errors
1 parent 3faecca commit ce86d59

35 files changed

+1325
-291
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.0.1)
2-
project(Jinja2Cpp VERSION 0.9.1)
2+
project(Jinja2Cpp VERSION 0.9.2)
33

44
if (${CMAKE_VERSION} VERSION_GREATER "3.12")
55
cmake_policy(SET CMP0074 OLD)

include/jinja2cpp/error_info.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ enum class ErrorCode
3030
UnexpectedExprBegin,
3131
UnexpectedExprEnd,
3232
UnexpectedStmtBegin,
33-
UnexpectedStmtEnd
33+
UnexpectedStmtEnd,
34+
TemplateNotFound,
35+
TemplateNotParsed,
36+
InvalidValueType,
3437
};
3538

3639
struct SourceLocation

include/jinja2cpp/template.h

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,25 @@ namespace jinja2
1515
class ITemplateImpl;
1616
class TemplateEnv;
1717
template<typename CharT> class TemplateImpl;
18-
using ParseResult = nonstd::expected<void, ErrorInfo>;
19-
using ParseResultW = nonstd::expected<void, ErrorInfoW>;
18+
template<typename U>
19+
using Result = nonstd::expected<U, ErrorInfo>;
20+
template<typename U>
21+
using ResultW = nonstd::expected<U, ErrorInfoW>;
2022

2123
class Template
2224
{
2325
public:
24-
Template(TemplateEnv* env = nullptr);
26+
Template() : Template(nullptr) {}
27+
explicit Template(TemplateEnv* env);
2528
~Template();
2629

27-
ParseResult Load(const char* tpl, std::string tplName = std::string());
28-
ParseResult Load(const std::string& str, std::string tplName = std::string());
29-
ParseResult Load(std::istream& stream, std::string tplName = std::string());
30-
ParseResult LoadFromFile(const std::string& fileName);
30+
Result<void> Load(const char* tpl, std::string tplName = std::string());
31+
Result<void> Load(const std::string& str, std::string tplName = std::string());
32+
Result<void> Load(std::istream& stream, std::string tplName = std::string());
33+
Result<void> LoadFromFile(const std::string& fileName);
3134

32-
void Render(std::ostream& os, const ValuesMap& params);
33-
std::string RenderAsString(const ValuesMap& params);
35+
Result<void> Render(std::ostream& os, const ValuesMap& params);
36+
Result<std::string> RenderAsString(const ValuesMap& params);
3437

3538
private:
3639
std::shared_ptr<ITemplateImpl> m_impl;
@@ -41,16 +44,17 @@ class Template
4144
class TemplateW
4245
{
4346
public:
44-
TemplateW(TemplateEnv* env = nullptr);
47+
TemplateW() : TemplateW(nullptr) {}
48+
explicit TemplateW(TemplateEnv* env);
4549
~TemplateW();
4650

47-
ParseResultW Load(const wchar_t* tpl, std::string tplName = std::string());
48-
ParseResultW Load(const std::wstring& str, std::string tplName = std::string());
49-
ParseResultW Load(std::wistream& stream, std::string tplName = std::string());
50-
ParseResultW LoadFromFile(const std::string& fileName);
51+
ResultW<void> Load(const wchar_t* tpl, std::string tplName = std::string());
52+
ResultW<void> Load(const std::wstring& str, std::string tplName = std::string());
53+
ResultW<void> Load(std::wistream& stream, std::string tplName = std::string());
54+
ResultW<void> LoadFromFile(const std::string& fileName);
5155

52-
void Render(std::wostream& os, const ValuesMap& params);
53-
std::wstring RenderAsString(const ValuesMap& params);
56+
ResultW<void> Render(std::wostream& os, const ValuesMap& params);
57+
ResultW<std::wstring> RenderAsString(const ValuesMap& params);
5458

5559
private:
5660
std::shared_ptr<ITemplateImpl> m_impl;

src/ast_visitor.h

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#ifndef AST_VISITOR_H
2+
#define AST_VISITOR_H
3+
4+
namespace jinja2
5+
{
6+
class RendererBase;
7+
class ExpressionEvaluatorBase;
8+
class Statement;
9+
class ForStatement;
10+
class IfStatement;
11+
class ElseBranchStatement;
12+
class SetStatement;
13+
class ParentBlockStatement;
14+
class BlockStatement;
15+
class ExtendsStatement;
16+
class IncludeStatement;
17+
class ImportStatement;
18+
class MacroStatement;
19+
class MacroCallStatement;
20+
class ComposedRenderer;
21+
class RawTextRenderer;
22+
class ExpressionRenderer;
23+
24+
class StatementVisitor;
25+
26+
class VisitableStatement
27+
{
28+
public:
29+
virtual void ApplyVisitor(StatementVisitor* visitor) = 0;
30+
virtual void ApplyVisitor(StatementVisitor* visitor) const = 0;
31+
};
32+
33+
#define VISITABLE_STATEMENT() \
34+
void ApplyVisitor(StatementVisitor* visitor) override {visitor->DoVisit(this);} \
35+
void ApplyVisitor(StatementVisitor* visitor) const override {visitor->DoVisit(this);} \
36+
37+
namespace detail
38+
{
39+
template<typename Base, typename Type>
40+
class VisitorIfaceImpl : public Base
41+
{
42+
public:
43+
using Base::DoVisit;
44+
45+
virtual void DoVisit(Type*) {}
46+
virtual void DoVisit(const Type*) {}
47+
};
48+
49+
template<typename Type>
50+
class VisitorIfaceImpl<void, Type>
51+
{
52+
public:
53+
virtual void DoVisit(Type*) {}
54+
virtual void DoVisit(const Type*) {}
55+
};
56+
57+
template<typename Base, typename ... Types>
58+
struct VisitorBaseImpl;
59+
60+
template<typename Base, typename T, typename ... Types>
61+
struct VisitorBaseImpl<Base, T, Types...>
62+
{
63+
using current_base = VisitorIfaceImpl<Base, T>;
64+
using base_type = typename VisitorBaseImpl<current_base, Types...>::base_type;
65+
};
66+
67+
template<typename Base, typename T>
68+
struct VisitorBaseImpl<Base, T>
69+
{
70+
using base_type = VisitorIfaceImpl<Base, T>;
71+
};
72+
73+
74+
template<typename ... Types>
75+
struct VisitorBase
76+
{
77+
using type = typename VisitorBaseImpl<void, Types ...>::base_type;
78+
};
79+
}
80+
81+
template<typename ... Types>
82+
using VisitorBase = typename detail::VisitorBase<Types...>::type;
83+
84+
class StatementVisitor : public VisitorBase<
85+
RendererBase,
86+
Statement,
87+
ForStatement,
88+
IfStatement,
89+
ElseBranchStatement,
90+
SetStatement,
91+
ParentBlockStatement,
92+
BlockStatement,
93+
ExtendsStatement,
94+
IncludeStatement,
95+
ImportStatement,
96+
MacroStatement,
97+
MacroCallStatement,
98+
ComposedRenderer,
99+
RawTextRenderer,
100+
ExpressionRenderer>
101+
{
102+
public:
103+
void Visit(VisitableStatement* stmt)
104+
{
105+
stmt->ApplyVisitor(this);
106+
}
107+
void Visit(const VisitableStatement* stmt)
108+
{
109+
stmt->ApplyVisitor(this);
110+
}
111+
};
112+
}
113+
114+
115+
#endif

src/error_info.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,15 @@ void RenderErrorInfo(std::basic_ostream<CharT>& os, const ErrorInfoTpl<CharT>& e
191191
case ErrorCode::UnexpectedStmtEnd:
192192
os << UNIVERSAL_STR("Unexpected statement block end");
193193
break;
194+
case ErrorCode::TemplateNotParsed:
195+
os << UNIVERSAL_STR("Template not parsed");
196+
break;
197+
case ErrorCode::TemplateNotFound:
198+
os << UNIVERSAL_STR("Template(s) not found: ") << errInfo.GetExtraParams()[0];
199+
break;
200+
case ErrorCode::InvalidValueType:
201+
os << UNIVERSAL_STR("Invalid value type");
202+
break;
194203
}
195204
os << std::endl << errInfo.GetLocationDescr();
196205
}

src/internal_value.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ struct OutputValueConvertor
507507
}
508508
result_t operator()(const Callable&) const {return result_t();}
509509
result_t operator()(const UserCallable&) const {return result_t();}
510-
result_t operator()(const RendererBase*) const {return result_t();}
510+
result_t operator()(const std::shared_ptr<RendererBase>&) const {return result_t();}
511511

512512
template<typename T>
513513
result_t operator()(const RecWrapper<T>& val) const

src/internal_value.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ struct KeyValuePair;
9595
class RendererBase;
9696

9797
class InternalValue;
98-
using InternalValueData = nonstd::variant<EmptyValue, bool, std::string, TargetString, int64_t, double, ValueRef, ListAdapter, MapAdapter, RecursiveWrapper<KeyValuePair>, RecursiveWrapper<Callable>, RendererBase*>;
98+
using InternalValueData = nonstd::variant<EmptyValue, bool, std::string, TargetString, int64_t, double, ValueRef, ListAdapter, MapAdapter, RecursiveWrapper<KeyValuePair>, RecursiveWrapper<Callable>, std::shared_ptr<RendererBase>>;
9999

100100
using InternalValueRef = ReferenceWrapper<InternalValue>;
101101
using InternalValueMap = std::unordered_map<std::string, InternalValue>;

src/lexer.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ struct Token
8585
Import,
8686
Recursive,
8787
Scoped,
88+
With,
89+
Without,
90+
Ignore,
91+
Missing,
92+
Context,
93+
From,
94+
As,
8895

8996
// Template control
9097
CommentBegin,
@@ -154,7 +161,14 @@ enum class Keyword
154161
Include,
155162
Import,
156163
Recursive,
157-
Scoped
164+
Scoped,
165+
With,
166+
Without,
167+
Ignore,
168+
Missing,
169+
Context,
170+
From,
171+
As,
158172
};
159173

160174
struct LexerHelper

src/lexertk.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ namespace lexertk
634634
scan_operator();
635635
return;
636636
}
637-
else if (traits::is_letter(*s_itr_))
637+
else if (traits::is_letter(*s_itr_) || ('_' == (*s_itr_)))
638638
{
639639
scan_symbol();
640640
return;

src/render_context.h

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ struct IRendererCallback
1818
virtual TargetString GetAsTargetString(const InternalValue& val) = 0;
1919
virtual OutStream GetStreamOnString(TargetString& str) = 0;
2020
virtual nonstd::variant<EmptyValue,
21-
nonstd::expected<std::shared_ptr<TemplateImpl<char>>, ErrorInfo>,
22-
nonstd::expected<std::shared_ptr<TemplateImpl<wchar_t>>, ErrorInfoW>> LoadTemplate(const std::string& fileName) const = 0;
21+
nonstd::expected<std::shared_ptr<TemplateImpl<char>>, ErrorInfo>,
22+
nonstd::expected<std::shared_ptr<TemplateImpl<wchar_t>>, ErrorInfoW>> LoadTemplate(const std::string& fileName) const = 0;
23+
virtual nonstd::variant<EmptyValue,
24+
nonstd::expected<std::shared_ptr<TemplateImpl<char>>, ErrorInfo>,
25+
nonstd::expected<std::shared_ptr<TemplateImpl<wchar_t>>, ErrorInfoW>> LoadTemplate(const InternalValue& fileName) const = 0;
26+
virtual void ThrowRuntimeError(ErrorCode code, ValuesList extraParams) = 0;
2327
};
2428

2529
class RenderContext
@@ -51,25 +55,29 @@ class RenderContext
5155

5256
auto FindValue(const std::string& val, bool& found) const
5357
{
54-
for (auto p = m_scopes.rbegin(); p != m_scopes.rend(); ++ p)
58+
auto finder = [&val, &found](auto& map) mutable
5559
{
56-
auto& map = *p;
57-
auto valP = map.find(val);
58-
if (valP != map.end())
59-
{
60+
auto p = map.find(val);
61+
if (p != map.end())
6062
found = true;
63+
64+
return p;
65+
};
66+
67+
if (m_boundScope)
68+
{
69+
auto valP = finder(*m_boundScope);
70+
if (found)
6171
return valP;
62-
}
6372
}
64-
auto valP = m_externalScope->find(val);
65-
if (valP != m_externalScope->end())
73+
74+
for (auto p = m_scopes.rbegin(); p != m_scopes.rend(); ++ p)
6675
{
67-
found = true;
68-
return valP;
76+
auto valP = finder(*p);
77+
if (found)
78+
return valP;
6979
}
70-
71-
found = false;
72-
return m_externalScope->end();
80+
return finder(*m_externalScope);
7381
}
7482

7583
auto& GetCurrentScope() const
@@ -92,16 +100,22 @@ class RenderContext
92100
RenderContext Clone(bool includeCurrentContext) const
93101
{
94102
if (!includeCurrentContext)
95-
return RenderContext(*m_externalScope, m_rendererCallback);
103+
return RenderContext(m_emptyScope, m_rendererCallback);
96104

97105
return RenderContext(*this);
98106
}
107+
108+
void BindScope(InternalValueMap* scope)
109+
{
110+
m_boundScope = scope;
111+
}
99112
private:
100113
InternalValueMap* m_currentScope;
101114
const InternalValueMap* m_externalScope;
115+
InternalValueMap m_emptyScope;
102116
std::list<InternalValueMap> m_scopes;
103117
IRendererCallback* m_rendererCallback;
104-
118+
const InternalValueMap* m_boundScope = nullptr;
105119
};
106120
} // jinja2
107121

0 commit comments

Comments
 (0)