Skip to content

Commit

Permalink
Merge pull request #14 from jhauris/forloop
Browse files Browse the repository at this point in the history
Forloop
  • Loading branch information
hughperkins committed May 8, 2018
2 parents 8e2b2b3 + e70fa0f commit 04196b0
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 49 deletions.
22 changes: 22 additions & 0 deletions README.md
Expand Up @@ -81,6 +81,28 @@ b[1] = image[1];
)d";
EXPECT_EQ( expectedResult, result );
```
```
string source = R"DELIM(
{% for i in its %}
a[{{i}}] = image[{{i}}];
{% endfor %}
)DELIM";

Template mytemplate( source );
mytemplate.setValue( "its", TupleValue::create(0, 1.1, "2abc") );
string result = mytemplate.render();
cout << result << endl;
string expectedResult = R"DELIM(

a[0] = image[0];

a[1.1] = image[1.1];

a[2abc] = image[2abc];

)DELIM";
EXPECT_EQ( expectedResult, result );
````
simple if condition:
```
Expand Down
93 changes: 49 additions & 44 deletions src/Jinja2CppLight.cpp
Expand Up @@ -142,67 +142,72 @@ int Template::eatSection( int pos, ControlSection *controlSection ) {
}
rangeString = replaceGlobal( rangeString, " ", "" );
vector<string> splitRangeString = split( rangeString, "(" );
if( splitRangeString[0] != "range" && splitRangeString.size() > 1 ) {
throw render_error("control section {% " + controlChange + " unexpected: third word should start with 'range' or be a variable name" );
}
std::shared_ptr<TupleValue> values;
if ( splitRangeString[0] == "range" ) {
if( splitRangeString[0] == "range" ) {
if( splitRangeString.size() != 2 ) {
throw render_error("control section " + controlChange + " unexpected: should be in format 'range(somevar)' or 'range(somenumber)'" );
}
string name = split( splitRangeString[1], ")" )[0];
// cout << "for range name: " << name << endl;
int endValue = 0;
// cout << "for range name: " << name << endl;
int endValue;
if( isNumber( name, &endValue ) ) {
} else {
auto p = valueByName.find( name );
if( p != valueByName.end() ) {
IntValue *intValue = dynamic_cast< IntValue * >( p->second.get() );
if ( intValue == 0 ) {
if( valueByName.find( name ) != valueByName.end() ) {
IntValue *intValue = dynamic_cast< IntValue * >( valueByName[ name ].get() );
if( intValue == 0 ) {
throw render_error("for loop range var " + name + " must be an int (but it's not)");
}
endValue = intValue->value;
} else {
throw render_error("for loop range var " + name + " not recognized");
}
}
}
int beginValue = 0; // default for now...
// cout << "for loop start=" << beginValue << " end=" << endValue << endl;
std::unique_ptr<ForRangeSection> forSection(new ForRangeSection());
forSection->startPos = controlChangeEnd + 2;
forSection->loopStart = beginValue;
forSection->loopEnd = endValue;
forSection->varName = varname;
pos = eatSection( controlChangeEnd + 2, forSection.get() );
size_t controlEndEndPos = sourceCode.find("%}", pos );
if( controlEndEndPos == string::npos ) {
throw render_error("No control end section found at: " + sourceCode.substr(pos ) );
}
values = std::make_shared<TupleValue>();
for ( int idx = 0; idx < endValue; ++ idx )
values->addValue(idx);
string controlEnd = sourceCode.substr( pos, controlEndEndPos - pos + 2 );
string controlEndNorm = replaceGlobal( controlEnd, " ", "" );
if( controlEndNorm != "{%endfor%}" ) {
throw render_error("No control end section found, expected '{% endfor %}', got '" + controlEnd + "'" );
}
forSection->endPos = controlEndEndPos + 2;
controlSection->sections.push_back(std::move(forSection));
pos = controlEndEndPos + 2;
} else {
auto &name = splitRangeString[0];
auto p = valueByName.find( name );
if( p != valueByName.end() ) {
auto tupleValue = std::dynamic_pointer_cast< TupleValue >( p->second );
if ( tupleValue ) {
values = tupleValue;
} else {
throw render_error("for loop range var " + name + " must be an int (but it's not)");
const std::string name = rangeString;
if (valueByName.find( name ) != valueByName.end() ) {
TupleValue *value = dynamic_cast< TupleValue * >( valueByName[ name ].get() );
if( value == 0 ) {
throw render_error("for loop var " + name + " must be a range or a vector (but it's neither)");
}
} else {
throw render_error("for loop range var " + name + " not recognized");
throw render_error("for loop var " + name + " not recognized");
}
std::unique_ptr<ForSection> forSection(new ForSection());
forSection->varName = varname;
forSection->tupVarName = name;

pos = eatSection( controlChangeEnd + 2, forSection.get() );
controlSection->sections.push_back(std::move(forSection));
size_t controlEndEndPos = sourceCode.find("%}", pos );
if( controlEndEndPos == string::npos ) {
throw render_error("No control end section found at: " + sourceCode.substr(pos ) );
}
string controlEnd = sourceCode.substr( pos, controlEndEndPos - pos + 2 );
string controlEndNorm = replaceGlobal( controlEnd, " ", "" );
if( controlEndNorm != "{%endfor%}" ) {
throw render_error("No control end section found, expected '{% endfor %}', got '" + controlEnd + "'" );
}
pos = controlEndEndPos + 2;
}
// cout << "for loop start=" << beginValue << " end=" << endValue << endl;
std::unique_ptr<ForSection> forSection(new ForSection());
forSection->startPos = controlChangeEnd + 2;
forSection->values = std::move(values);
forSection->varName = varname;
pos = eatSection( controlChangeEnd + 2, forSection.get() );
size_t controlEndEndPos = sourceCode.find("%}", pos );
if( controlEndEndPos == string::npos ) {
throw render_error("No control end section found at: " + sourceCode.substr(pos ) );
}
string controlEnd = sourceCode.substr( pos, controlEndEndPos - pos + 2 );
string controlEndNorm = replaceGlobal( controlEnd, " ", "" );
if( controlEndNorm != "{%endfor%}" ) {
throw render_error("No control end section found, expected '{% endfor %}', got '" + controlEnd + "'" );
}
forSection->endPos = controlEndEndPos + 2;
controlSection->sections.push_back(std::move(forSection));
pos = controlEndEndPos + 2;
// tokenStack.push_back("for");
// varNameStack.push_back(name);
} else if (splitControlChange[0] == "if") {
std::unique_ptr<Code> code(new Code());
code->startPos = pos;
Expand Down
47 changes: 42 additions & 5 deletions src/Jinja2CppLight.h
Expand Up @@ -202,9 +202,10 @@ class Container : public ControlSection {
}
};

class ForSection : public ControlSection {
class ForRangeSection : public ControlSection {
public:
std::shared_ptr<TupleValue> values;
int loopStart;
int loopEnd;
std::string varName;
int startPos;
int endPos;
Expand All @@ -214,13 +215,14 @@ class ForSection : public ControlSection {
if( valueByName.find( varName ) != valueByName.end() ) {
throw render_error("variable " + varName + " already exists in this context" );
}
for( auto &v : values->values ) {
valueByName[varName] = v;
valueByName[varName] = std::make_shared<IntValue>( 0 );
for (auto i = loopStart; i < loopEnd; ++i ){
dynamic_cast<IntValue*>(valueByName[varName].get())->value = i;
for( size_t j = 0; j < sections.size(); j++ ) {
result += sections[j]->render( valueByName );
}
valueByName.erase( varName );
}
valueByName.erase( varName );
return result;
}
//Container *contents;
Expand All @@ -233,6 +235,41 @@ class ForSection : public ControlSection {
}
};

class ForSection : public ControlSection {
public:
std::string varName;
std::string tupVarName;
virtual std::string render( ValueMap &valueByName ) {
std::string result = "";
if( valueByName.find( varName ) != valueByName.end() ) {
throw render_error("variable " + varName + " already exists in this context" );
}

const std::shared_ptr<Value> &val = valueByName.at( tupVarName ); // throws if something happened to the TupleValue
const TupleValue *tupValue = dynamic_cast< TupleValue * >( val.get() );
if (!tupValue) {
throw render_error("variable " + tupVarName + " no longer valid in context" );
}
const std::vector<std::shared_ptr<Value>> &tupValues = tupValue->values;
for ( auto itr = tupValues.cbegin(); itr != tupValues.cend(); ++itr ) {
valueByName[ varName ] = *itr;
for( std::size_t j = 0; j < sections.size(); ++j) {
result += sections[j]->render( valueByName );
}
valueByName.erase( varName );
}

return result;
}
virtual void print( std::string prefix ) {
std::cout << prefix << "For ( " << varName << " in " << tupVarName << " ) {" << std::endl;
for( std::size_t i = 0; i < sections.size(); i++ ){
sections[i]->print( prefix + " " );
}
std::cout << prefix << "}" << std::endl;
}
};

class Code : public ControlSection {
public:
// vector< ControlSection * >sections;
Expand Down
54 changes: 54 additions & 0 deletions test/testJinja2CppLight.cpp
Expand Up @@ -6,6 +6,7 @@

#include <iostream>
#include <string>
#include <vector>

#include "gtest/gtest.h"
#include "test/gtest_supp.h"
Expand Down Expand Up @@ -34,6 +35,59 @@ TEST( testJinja2CppLight, basicsubstitution ) {
)DELIM";
EXPECT_EQ( expectedResult, result );
}
/*
TEST( testJinja2CppLight, vectorsubstitution ) {
string source = R"DELIM(
Here's the list: {{ values }}...
{% if maybe %}{{ maybe }}{% endif %}
{% if maybenot %}{{ maybenot }}{% endif %}
We're going to print it anyway: {{ maybenot }}
)DELIM";
std::vector<std::string> values = { "one", "two", "three" };
std::vector<std::string> maybe = { "There is one" };
std::vector<std::string> maybenot;
Template mytemplate( source );
mytemplate.setValue( "values", values );
mytemplate.setValue( "maybe", maybe );
mytemplate.setValue( "maybenot", maybenot );
string result = mytemplate.render();
const string expectedResult = R"DELIM(
Here's the list: one two three...
There is one
We're going to print it anyway:
)DELIM";
EXPECT_EQ( expectedResult, result );
}
TEST( testJinja2CppLight, forloop ) {
string source = R"DELIM(
Shopping list:{% for item in items %}
- {{ item }}{% endfor %}
)DELIM";
std::vector<std::string> shopList = { "eggs", "milk", "vodka" };
Template mytemplate0( source );
mytemplate0.setValue( "items", shopList );
string result = mytemplate0.render();
string expectedResult = R"DELIM(
Shopping list:
- eggs
- milk
- vodka
)DELIM";
EXPECT_EQ( expectedResult, result );
shopList.clear();
Template mytemplate1( source );
mytemplate1.setValue( "items", shopList );
result = mytemplate1.render();
expectedResult = R"DELIM(
Shopping list:
)DELIM";
EXPECT_EQ( expectedResult, result );
}
*/
TEST( testSpeedTemplates, namemissing ) {
string source = R"DELIM(
This is my {{avalue}} template.
Expand Down

0 comments on commit 04196b0

Please sign in to comment.