Repository: PebbleTemplates/pebble
Pull request #271 (2017-05-25 13:01:05)

Add support for 'as' and 'from' in import statement
for issue #209 

flof: That's great! I will give it a try.
yanxiyue: hi @ebussieres,  could give some response?
tx-francesco-figari: @mbosecke @ebussieres 
I'm also interested in this, it's would help me share templates between Pebble and Twig. Is there anything we can help with to get this through?
cjbrooks12: As it appears this repo is dying, I have decied to create and maintain a new fork of Pebble at [JavaEden/Pebble](https://github.com/JavaEden/pebble), and I would love to get this PR merged into the new repo. Please take this PR and open a new one to the `dev` branch of `JavaEden/pebble`.

The future of Pebble has been shaky for a while now, but I refuse to let Pebble die. It is a great project, and I hope to give it same love and care that the original maintainers have as they built Pebble. If you have any questions about the future of Pebble, please reach out on Gitter. Let's keep this community alive, I hope to see you over at the new location!

**New Pebble Home**
- Original announcement of new ownership: https://github.com/PebbleTemplates/pebble/issues/311#issuecomment-379496941
- https://github.com/JavaEden/pebble
- https://bintray.com/javaeden/Eden/Pebble
- https://gitter.im/JavaEden/pebble
ebussieres: It seems to cause some performance issue. About 10-15% drop in performance
yanxiyue: ok, I will try the benchmark later. 
yanxiyue: I did the benchmark test in my development environment. 
Compared to the master branch, the results show that there is not much difference in performance. 
The following are my environmental information and test data. 
And I have move the the MacroAttributeProvider branch down. plz take one more try under your environment. @ebussieres 

```java
// env info
java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

Intel Xeon E312xx (Sandy Bridge)
Linux 2.6.32-642.1.1.el6.x86_64 #1 SMP Tue May 31 21:57:07 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux


// results
// benchmarks-0.jar: version 2.2.0
Benchmark          Mode  Cnt      Score     Error  Units
Pebble.benchmark  thrpt   50  13080.610 ± 238.601  ops/s
Pebble.benchmark  thrpt   50  12950.239 ± 531.155  ops/s

// benchmarks-1.jar: origin master
Benchmark          Mode  Cnt      Score     Error  Units
Pebble.benchmark  thrpt   50  11559.832 ± 414.224  ops/s
Pebble.benchmark  thrpt   50  11423.473 ± 343.797  ops/s

// benchmarks-2.jar: Added support for 'as' and 'from' in import statement
Benchmark          Mode  Cnt      Score     Error  Units
Pebble.benchmark  thrpt   50  11231.329 ± 318.845  ops/s
Pebble.benchmark  thrpt   50  11705.103 ± 360.210  ops/s

// benchmarks-3.jar: Move the MacroAttributeProvider branch down.
Benchmark          Mode  Cnt      Score     Error  Units
Pebble.benchmark  thrpt   50  11372.813 ± 458.316  ops/s
Pebble.benchmark  thrpt   50  11421.412 ± 187.204  ops/s
```
ebussieres: @yanxiyue It seems that some tests are missing here. Cases when there's a name clash with the alias if it's already defined for example. Please add these tests. Except for that, it's clean and i'll merge it when more tests is gonna be added.

Thanks !
yanxiyue: ok， i will add some test cases in the next days.

Patch:
diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java b/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java
index be323c8c..cc4d1575 100644
--- a/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java
+++ b/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java
@@ -46,6 +46,7 @@ import com.mitchellbosecke.pebble.tokenParser.ExtendsTokenParser;
 import com.mitchellbosecke.pebble.tokenParser.FilterTokenParser;
 import com.mitchellbosecke.pebble.tokenParser.FlushTokenParser;
 import com.mitchellbosecke.pebble.tokenParser.ForTokenParser;
+import com.mitchellbosecke.pebble.tokenParser.FromTokenParser;
 import com.mitchellbosecke.pebble.tokenParser.IfTokenParser;
 import com.mitchellbosecke.pebble.tokenParser.ImportTokenParser;
 import com.mitchellbosecke.pebble.tokenParser.IncludeTokenParser;
@@ -76,6 +77,7 @@ public class CoreExtension extends AbstractExtension {
         parsers.add(new ParallelTokenParser());
         parsers.add(new SetTokenParser());
         parsers.add(new CacheTokenParser());
+        parsers.add(new FromTokenParser());
 
         // verbatim tag is implemented directly in the LexerImpl
         return parsers;
diff --git a/src/main/java/com/mitchellbosecke/pebble/node/FromNode.java b/src/main/java/com/mitchellbosecke/pebble/node/FromNode.java
new file mode 100644
index 00000000..6ea9bb39
--- /dev/null
+++ b/src/main/java/com/mitchellbosecke/pebble/node/FromNode.java
@@ -0,0 +1,43 @@
+package com.mitchellbosecke.pebble.node;
+
+import java.io.Writer;
+import java.util.List;
+
+import com.mitchellbosecke.pebble.error.PebbleException;
+import com.mitchellbosecke.pebble.extension.NodeVisitor;
+import com.mitchellbosecke.pebble.node.expression.Expression;
+import com.mitchellbosecke.pebble.template.EvaluationContext;
+import com.mitchellbosecke.pebble.template.PebbleTemplateImpl;
+import com.mitchellbosecke.pebble.utils.Pair;
+
+/**
+ * From Node for <br/>
+ * 
+ * <p>{% from "templateName" import macroName as alias %}<p>
+ * 
+ * @author yanxiyue
+ *
+ */
+public class FromNode extends AbstractRenderableNode {
+
+    private final Expression<?> fromExpression;
+    private final List<Pair<String, String>> namedMacros;
+
+    public FromNode(int lineNumber, Expression<?> fromExpression, List<Pair<String, String>> namedMacros) {
+        super(lineNumber);
+        this.fromExpression = fromExpression;
+        this.namedMacros = namedMacros;
+    }
+
+    @Override
+    public void render(PebbleTemplateImpl self, Writer writer, final EvaluationContext context) {
+        String templateName = (String) fromExpression.evaluate(self, context);
+        self.importNamedMacrosFromTemplate(context, templateName, namedMacros);
+    }
+
+    @Override
+    public void accept(NodeVisitor visitor) {
+        visitor.visit(this);
+    }
+
+}
diff --git a/src/main/java/com/mitchellbosecke/pebble/node/ImportNode.java b/src/main/java/com/mitchellbosecke/pebble/node/ImportNode.java
index f7869958..4fd225dc 100644
--- a/src/main/java/com/mitchellbosecke/pebble/node/ImportNode.java
+++ b/src/main/java/com/mitchellbosecke/pebble/node/ImportNode.java
@@ -11,6 +11,7 @@ package com.mitchellbosecke.pebble.node;
 import com.mitchellbosecke.pebble.extension.NodeVisitor;
 import com.mitchellbosecke.pebble.node.expression.Expression;
 import com.mitchellbosecke.pebble.template.EvaluationContext;
+import com.mitchellbosecke.pebble.template.MacroAttributeProvider;
 import com.mitchellbosecke.pebble.template.PebbleTemplateImpl;
 
 import java.io.Writer;
@@ -18,15 +19,27 @@ import java.io.Writer;
 public class ImportNode extends AbstractRenderableNode {
 
     private final Expression<?> importExpression;
+    private final String alias;
 
-    public ImportNode(int lineNumber, Expression<?> importExpression) {
+    public ImportNode(int lineNumber, Expression<?> importExpression, String alias) {
         super(lineNumber);
         this.importExpression = importExpression;
+        this.alias = alias;
     }
 
     @Override
     public void render(PebbleTemplateImpl self, Writer writer, EvaluationContext context) {
-        self.importTemplate(context, (String) importExpression.evaluate(self, context));
+        String templateName = (String) importExpression.evaluate(self, context);
+        if (alias != null) {
+            self.importNamedTemplate(context, templateName, alias);
+
+            // put the imported template into scope
+            PebbleTemplateImpl template = self.getNamedImportedTemplate(context, alias);
+            context.getScopeChain().put(alias, new MacroAttributeProvider(template));
+
+        } else {
+            self.importTemplate(context, templateName);
+        }
     }
 
     @Override
diff --git a/src/main/java/com/mitchellbosecke/pebble/node/expression/GetAttributeExpression.java b/src/main/java/com/mitchellbosecke/pebble/node/expression/GetAttributeExpression.java
index e850eb7e..0c407b53 100644
--- a/src/main/java/com/mitchellbosecke/pebble/node/expression/GetAttributeExpression.java
+++ b/src/main/java/com/mitchellbosecke/pebble/node/expression/GetAttributeExpression.java
@@ -23,6 +23,7 @@ import com.mitchellbosecke.pebble.extension.NodeVisitor;
 import com.mitchellbosecke.pebble.node.ArgumentsNode;
 import com.mitchellbosecke.pebble.node.PositionalArgumentNode;
 import com.mitchellbosecke.pebble.template.EvaluationContext;
+import com.mitchellbosecke.pebble.template.MacroAttributeProvider;
 import com.mitchellbosecke.pebble.template.PebbleTemplateImpl;
 
 import java.lang.reflect.AccessibleObject;
@@ -157,6 +158,12 @@ public class GetAttributeExpression implements Expression<Object> {
                     return dynamicAttributeProvider.getDynamicAttribute(attributeNameValue, argumentValues);
                 }
             }
+            
+            // check if the object should provide the attribute by macro invocation
+            if (object instanceof MacroAttributeProvider) {
+                MacroAttributeProvider macroAttributeProvider = (MacroAttributeProvider) object;
+                return macroAttributeProvider.macro(context, attributeName, args, false, this.lineNumber);
+            }
 
             /*
              * turn args into an array of types and an array of values in order
diff --git a/src/main/java/com/mitchellbosecke/pebble/template/EvaluationContext.java b/src/main/java/com/mitchellbosecke/pebble/template/EvaluationContext.java
index f0e128d1..9d280860 100644
--- a/src/main/java/com/mitchellbosecke/pebble/template/EvaluationContext.java
+++ b/src/main/java/com/mitchellbosecke/pebble/template/EvaluationContext.java
@@ -11,11 +11,14 @@ package com.mitchellbosecke.pebble.template;
 import com.google.common.cache.Cache;
 
 import com.mitchellbosecke.pebble.cache.CacheKey;
+import com.mitchellbosecke.pebble.error.PebbleException;
 import com.mitchellbosecke.pebble.extension.ExtensionRegistry;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.concurrent.ExecutorService;
 
 /**
@@ -71,6 +74,11 @@ public class EvaluationContext {
      */
     private final List<PebbleTemplateImpl> importedTemplates;
 
+    /**
+     * The named imported templates are used to look up macros.
+     */
+    private final Map<String, PebbleTemplateImpl> namedImportedTemplates;
+
     /**
      * toggle to enable/disable getClass access
      */
@@ -90,7 +98,8 @@ public class EvaluationContext {
      */
     public EvaluationContext(PebbleTemplateImpl self, boolean strictVariables, Locale locale,
                              ExtensionRegistry extensionRegistry, Cache<CacheKey, Object> tagCache,
-                             ExecutorService executorService, List<PebbleTemplateImpl> importedTemplates, ScopeChain scopeChain,
+                             ExecutorService executorService, List<PebbleTemplateImpl> importedTemplates,
+                             Map<String, PebbleTemplateImpl> namedImportedTemplates, ScopeChain scopeChain,
                              Hierarchy hierarchy, boolean allowGetClass) {
 
         if (hierarchy == null) {
@@ -103,6 +112,7 @@ public class EvaluationContext {
         this.tagCache = tagCache;
         this.executorService = executorService;
         this.importedTemplates = importedTemplates;
+        this.namedImportedTemplates = namedImportedTemplates;
         this.scopeChain = scopeChain;
         this.hierarchy = hierarchy;
         this.allowGetClass = allowGetClass;
@@ -117,7 +127,7 @@ public class EvaluationContext {
      */
     public EvaluationContext shallowCopyWithoutInheritanceChain(PebbleTemplateImpl self) {
         EvaluationContext result = new EvaluationContext(self, strictVariables, locale, extensionRegistry, tagCache,
-                executorService, importedTemplates, scopeChain, null, allowGetClass);
+                executorService, importedTemplates, namedImportedTemplates, scopeChain, null, allowGetClass);
         return result;
     }
 
@@ -130,8 +140,8 @@ public class EvaluationContext {
      * @return A copy of the evaluation context
      */
     public EvaluationContext threadSafeCopy(PebbleTemplateImpl self) {
-        EvaluationContext result = new EvaluationContext(self, strictVariables, locale, extensionRegistry, tagCache,
-                executorService, new ArrayList<>(importedTemplates), scopeChain.deepCopy(), hierarchy, allowGetClass);
+        EvaluationContext result = new EvaluationContext(self, strictVariables, locale, extensionRegistry, tagCache, executorService,
+                new ArrayList<>(importedTemplates), new HashMap<>(namedImportedTemplates), scopeChain.deepCopy(), hierarchy, allowGetClass);
         return result;
     }
 
@@ -180,6 +190,22 @@ public class EvaluationContext {
         return this.importedTemplates;
     }
 
+    /**
+     * Returns the named imported template.
+     *
+     * @return the named imported template.
+     */
+    public PebbleTemplateImpl getNamedImportedTemplate(String alias) {
+        return this.namedImportedTemplates.get(alias);
+    }
+
+    public void addNamedImportedTemplates(String alias, PebbleTemplateImpl template) {
+        if (namedImportedTemplates.containsKey(alias)) {
+            throw new PebbleException(null, "More than one named template can not share the same name: " + alias);
+        }
+        this.namedImportedTemplates.put(alias, template);
+    }
+
     /**
      * Returns the cache used for the "cache" tag
      *
diff --git a/src/main/java/com/mitchellbosecke/pebble/template/MacroAttributeProvider.java b/src/main/java/com/mitchellbosecke/pebble/template/MacroAttributeProvider.java
new file mode 100644
index 00000000..1f671f0e
--- /dev/null
+++ b/src/main/java/com/mitchellbosecke/pebble/template/MacroAttributeProvider.java
@@ -0,0 +1,27 @@
+package com.mitchellbosecke.pebble.template;
+
+import com.mitchellbosecke.pebble.error.PebbleException;
+import com.mitchellbosecke.pebble.node.ArgumentsNode;
+
+
+public class MacroAttributeProvider {
+    private final PebbleTemplateImpl template;
+
+    public MacroAttributeProvider(PebbleTemplateImpl template) {
+        this.template = template;
+    }
+
+    /**
+     * Invokes a macro
+     *
+     * @param context         The evaluation context
+     * @param macroName       The name of the macro
+     * @param args            The arguments
+     * @param ignoreOverriden Whether or not to ignore macro definitions in child template
+     * @return The results of the macro invocation
+     */
+    public Object macro(EvaluationContext context, String macroName, ArgumentsNode args, boolean ignoreOverriden, int lineNumber) {
+        return template.macro(context, macroName, args, ignoreOverriden, lineNumber);
+    }
+
+}
diff --git a/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplateImpl.java b/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplateImpl.java
index 6c69fc6d..8894c38d 100644
--- a/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplateImpl.java
+++ b/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplateImpl.java
@@ -14,11 +14,13 @@ import com.mitchellbosecke.pebble.extension.escaper.SafeString;
 import com.mitchellbosecke.pebble.node.ArgumentsNode;
 import com.mitchellbosecke.pebble.node.RootNode;
 import com.mitchellbosecke.pebble.utils.FutureWriter;
+import com.mitchellbosecke.pebble.utils.Pair;
 
 import java.io.IOException;
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -174,7 +176,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
 
         return new EvaluationContext(this, this.engine.isStrictVariables(), locale,
                 this.engine.getExtensionRegistry(), this.engine.getTagCache(), this.engine.getExecutorService(),
-                new ArrayList<PebbleTemplateImpl>(), scopeChain, null, this.engine.isAllowGetClass());
+                new ArrayList<PebbleTemplateImpl>(), new HashMap<String, PebbleTemplateImpl>(), scopeChain, null, this.engine.isAllowGetClass());
     }
 
     /**
@@ -187,6 +189,43 @@ public class PebbleTemplateImpl implements PebbleTemplate {
         context.getImportedTemplates().add((PebbleTemplateImpl) this.engine.getTemplate(this.resolveRelativePath(name)));
     }
 
+    /**
+     * Imports a named template.
+     *
+     * @param context The evaluation context
+     * @param name    The template name
+     * @param alias   The template alias
+     */
+    public void importNamedTemplate(EvaluationContext context, String name, String alias) {
+        context.addNamedImportedTemplates(alias, (PebbleTemplateImpl) this.engine.getTemplate(this.resolveRelativePath(name)));
+    }
+
+    /**
+     * Imports named macros from specified template.
+     *
+     * @param context     The evaluation context
+     * @param name        The template name
+     * @param namedMacros named macros
+     * @throws PebbleException
+     */
+    public void importNamedMacrosFromTemplate(EvaluationContext context, String name, List<Pair<String, String>> namedMacros) {
+        PebbleTemplateImpl templateImpl = (PebbleTemplateImpl) this.engine.getTemplate(this.resolveRelativePath(name));
+        for (Pair<String, String> pair : namedMacros) {
+            Macro m = templateImpl.macros.get(pair.getRight());
+            registerMacro(pair.getLeft(), m);
+        }
+    }
+
+    /**
+     * Returns a named template.
+     *
+     * @param context The evaluation context
+     * @param alias   The template alias
+     */
+    public PebbleTemplateImpl getNamedImportedTemplate(EvaluationContext context, String alias) {
+        return context.getNamedImportedTemplate(alias);
+    }
+
     /**
      * Includes a template with {@code name} into this template.
      *
@@ -266,6 +305,19 @@ public class PebbleTemplateImpl implements PebbleTemplate {
         this.macros.put(macro.getName(), macro);
     }
 
+    /**
+     * Registers a macro with alias
+     *
+     * @param macro The macro
+     * @throws PebbleException Throws exception if macro already exists with the same name
+     */
+    public void registerMacro(String alias, Macro macro) {
+        if (this.macros.containsKey(alias)) {
+            throw new PebbleException(null, "More than one macro can not share the same name: " + alias);
+        }
+        this.macros.put(alias, macro);
+    }
+
     /**
      * A typical block declaration will use this method which evaluates the
      * block using the regular user-provided writer.
diff --git a/src/main/java/com/mitchellbosecke/pebble/tokenParser/FromTokenParser.java b/src/main/java/com/mitchellbosecke/pebble/tokenParser/FromTokenParser.java
new file mode 100644
index 00000000..9b045ddb
--- /dev/null
+++ b/src/main/java/com/mitchellbosecke/pebble/tokenParser/FromTokenParser.java
@@ -0,0 +1,88 @@
+package com.mitchellbosecke.pebble.tokenParser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.mitchellbosecke.pebble.error.ParserException;
+import com.mitchellbosecke.pebble.lexer.Token;
+import com.mitchellbosecke.pebble.lexer.TokenStream;
+import com.mitchellbosecke.pebble.node.FromNode;
+import com.mitchellbosecke.pebble.node.RenderableNode;
+import com.mitchellbosecke.pebble.node.expression.Expression;
+import com.mitchellbosecke.pebble.parser.Parser;
+import com.mitchellbosecke.pebble.utils.Pair;
+
+/**
+ * From Token parser for <br/>
+ * 
+ * <p>
+ * {% from "templateName" import macroName as alias %}
+ * <p>
+ * 
+ * @author yanxiyue
+ *
+ */
+public class FromTokenParser extends AbstractTokenParser {
+
+    @Override
+    public RenderableNode parse(Token token, Parser parser) {
+
+        TokenStream stream = parser.getStream();
+        int lineNumber = token.getLineNumber();
+
+        // skip over the 'from' token
+        stream.next();
+
+        // parse the teamplateName expression
+        Expression<?> fromExpression = parser.getExpressionParser().parseExpression();
+
+        // parse the import
+        List<Pair<String, String>> namedMacros = parseNamedMacros(parser);
+
+        stream.expect(Token.Type.EXECUTE_END);
+
+        return new FromNode(lineNumber, fromExpression, namedMacros);
+    }
+
+    private List<Pair<String, String>> parseNamedMacros(Parser parser) {
+        List<Pair<String, String>> pairs = new ArrayList<>();
+        TokenStream stream = parser.getStream();
+
+        stream.expect(Token.Type.NAME, "import");
+
+        Token pre = null, post = null;
+        while (!stream.current().getType().equals(Token.Type.EXECUTE_END)) {
+            pre = stream.expect(Token.Type.NAME);
+            if (stream.current().test(Token.Type.NAME, "as")) {
+                // Skips over 'as'
+                stream.next();
+
+                post = stream.expect(Token.Type.NAME);
+
+                pairs.add(new Pair<String, String>(post.getValue(), pre.getValue()));
+            } else {
+                pairs.add(new Pair<String, String>(pre.getValue(), pre.getValue()));
+            }
+
+            Token token = stream.current();
+            if (token.test(Token.Type.PUNCTUATION, ",")) {
+                // Skips over ','
+                stream.next();
+            } else if (token.getType().equals(Token.Type.EXECUTE_END)) {
+                break;
+
+            } else {
+                String message = String.format("Unexpected token of value \"%s\" and type %s, expected token of type %s or ',' ",
+                                token.getValue(), token.getType().toString(), Token.Type.EXECUTE_END);
+                throw new ParserException(null, message, token.getLineNumber(), stream.getFilename());
+            }
+        }
+
+        return pairs;
+    }
+
+    @Override
+    public String getTag() {
+        return "from";
+    }
+}
diff --git a/src/main/java/com/mitchellbosecke/pebble/tokenParser/ImportTokenParser.java b/src/main/java/com/mitchellbosecke/pebble/tokenParser/ImportTokenParser.java
index ce09e2da..50e2658d 100644
--- a/src/main/java/com/mitchellbosecke/pebble/tokenParser/ImportTokenParser.java
+++ b/src/main/java/com/mitchellbosecke/pebble/tokenParser/ImportTokenParser.java
@@ -27,10 +27,23 @@ public class ImportTokenParser extends AbstractTokenParser {
         stream.next();
 
         Expression<?> importExpression = parser.getExpressionParser().parseExpression();
+        
+        Token current = stream.current();
+        String alias = null;
+
+        // We check if there is an optional 'as' keyword on the import tag.
+        if (current.getType().equals(Token.Type.NAME) && current.getValue().equals("as")) {
+            
+            // Skip over 'as'
+            stream.next();
+            
+            current = stream.expect(Token.Type.NAME);
+            alias = current.getValue();
+        }
 
         stream.expect(Token.Type.EXECUTE_END);
 
-        return new ImportNode(lineNumber, importExpression);
+        return new ImportNode(lineNumber, importExpression, alias);
     }
 
     @Override


Test patch:
diff --git a/src/test/java/com/mitchellbosecke/pebble/macro/TestMacroCalls.java b/src/test/java/com/mitchellbosecke/pebble/macro/TestMacroCalls.java
index b0152d7c..7a79b1c9 100644
--- a/src/test/java/com/mitchellbosecke/pebble/macro/TestMacroCalls.java
+++ b/src/test/java/com/mitchellbosecke/pebble/macro/TestMacroCalls.java
@@ -11,6 +11,7 @@ import java.io.StringWriter;
 import java.io.Writer;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 /**
@@ -48,7 +49,76 @@ public class TestMacroCalls {
     // We expect, that the TestFilter was called 2x
     assertEquals(2, TestFilter.getCounter());
   }
+  
+  @Test
+  public void testMacroCallsWithImportAs() throws PebbleException, IOException {
+    // Build a pebble engine with one configured filter ("testfilter" - TestFilter.java)
+    PebbleEngine pebble = new PebbleEngine.Builder()
+            .extension(new PebbleExtension())
+            .strictVariables(false).build();
+    // Resets the counter of the filter
+    TestFilter.counter = 0;
+        /*
+         * Runs the test scenario:
+         *  import.as-template with an import-as of the macro and a call to this macro (Call #1)
+         *  import.as-templates includes "include.peb"
+         *  
+         *  include.peb with an import of the macro and a call to this macro (Call #2)
+         *  
+         *  We track the number of macro-calls by using a small "Filter" (TestFilter) that just
+         *  counts, how often it is called.
+         */
+    PebbleTemplate template = pebble.getTemplate("templates/macros/import.as.peb");
+    Writer writer = new StringWriter();
+    template.evaluate(writer);
+
 
+    // We expect, that the TestFilter was called 2x
+    assertEquals(2, TestFilter.getCounter());
+  }
+  
+  @Test
+  public void testMacroCallsWithFromToken() throws PebbleException, IOException {
+    // Build a pebble engine with one configured filter ("testfilter" - TestFilter.java)
+    PebbleEngine pebble = new PebbleEngine.Builder()
+            .extension(new PebbleExtension())
+            .strictVariables(false).build();
+    // Resets the counter of the filter
+    TestFilter.counter = 0;
+        /*
+         * Runs the test scenario:
+         *  from-template with an from-import-as of these macro and a call to these macro (Call #1, Call #3)
+         *  from-templates includes "include.peb"
+         *  
+         *  include.peb with an import of the macro and a call to this macro (Call #2)
+         *  
+         *  We track the number of macro-calls by using a small "Filter" (TestFilter) that just
+         *  counts, how often it is called.
+         */
+    PebbleTemplate template = pebble.getTemplate("templates/macros/from.peb");
+    Writer writer = new StringWriter();
+    template.evaluate(writer);
+
+
+    // We expect, that the TestFilter was called 3x
+    assertEquals(3, TestFilter.getCounter());
+  }
+  
+  @Test
+  public void testInvalidMacroWithFromToken() throws IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder().build();
+
+    try {
+      PebbleTemplate template = pebble.getTemplate("templates/macros/invalid.from.peb");
+      Writer writer = new StringWriter();
+      template.evaluate(writer);
+      fail("expected PebbleException");
+    } catch (PebbleException e) {
+      assertEquals(e.getLineNumber(), (Integer) 3);
+      assertEquals(e.getFileName(), "templates/macros/invalid.from.peb");
+    }
+  }
+  
   @Test
   public void testInvalidMacro() throws IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().build();
@@ -63,4 +133,32 @@ public class TestMacroCalls {
       assertEquals(e.getFileName(), "templates/macros/invalid.macro.peb");
     }
   }
+  
+  @Test
+  public void testInvalidSameAliasMacroWithFromToken() throws IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder().build();
+
+    try {
+      PebbleTemplate template = pebble.getTemplate("templates/macros/invalid.from.sameAlias.peb");
+      Writer writer = new StringWriter();
+      template.evaluate(writer);
+      fail("expected PebbleException");
+    } catch (PebbleException e) {
+      assertTrue(e.getPebbleMessage().startsWith("More than one macro can not share the same name"));
+    }
+  }
+  
+  @Test
+  public void testInvalidSameAliasMacroWithImportAsToken() throws IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder().build();
+
+    try {
+      PebbleTemplate template = pebble.getTemplate("templates/macros/invalid.import.as.sameAlias.peb");
+      Writer writer = new StringWriter();
+      template.evaluate(writer);
+      fail("expected PebbleException");
+    } catch (PebbleException e) {
+      assertTrue(e.getPebbleMessage().startsWith("More than one named template can not share the same name"));
+    }
+  }
 }
diff --git a/src/test/resources/templates/macros/from.peb b/src/test/resources/templates/macros/from.peb
new file mode 100644
index 00000000..a13cdce6
--- /dev/null
+++ b/src/test/resources/templates/macros/from.peb
@@ -0,0 +1,10 @@
+Hello
+
+{% from "templates/macros/macro.peb" import test as macro_test, test2 as macro_test2 %}
+Call 1: {{ macro_test() }}
+
+Call 3: {{ macro_test2() }}
+
+{% include "templates/macros/include.peb" %}
+
+
diff --git a/src/test/resources/templates/macros/import.as.peb b/src/test/resources/templates/macros/import.as.peb
new file mode 100644
index 00000000..4c242aff
--- /dev/null
+++ b/src/test/resources/templates/macros/import.as.peb
@@ -0,0 +1,6 @@
+Hello
+
+{% import "templates/macros/macro.peb" as macro %}
+Call 1: {{ macro.test() }}
+
+{% include "templates/macros/include.peb" %}
diff --git a/src/test/resources/templates/macros/invalid.from.peb b/src/test/resources/templates/macros/invalid.from.peb
new file mode 100644
index 00000000..12022625
--- /dev/null
+++ b/src/test/resources/templates/macros/invalid.from.peb
@@ -0,0 +1,10 @@
+Hello
+
+{% from "templates/macros/macro.peb" import test as macro_test test2 as macro_test2 %}
+Call 1: {{ macro_test() }}
+
+Call 3: {{ macro_test2() }}
+
+{% include "templates/macros/include.peb" %}
+
+
diff --git a/src/test/resources/templates/macros/invalid.from.sameAlias.peb b/src/test/resources/templates/macros/invalid.from.sameAlias.peb
new file mode 100644
index 00000000..67a58e60
--- /dev/null
+++ b/src/test/resources/templates/macros/invalid.from.sameAlias.peb
@@ -0,0 +1,11 @@
+Hello
+
+{% from "templates/macros/macro.peb" import test as macro_test%}
+{% from "templates/macros/macro.peb" import test2 as macro_test%}
+Call 1: {{ macro_test() }}
+
+Call 3: {{ macro_test2() }}
+
+{% include "templates/macros/include.peb" %}
+
+
diff --git a/src/test/resources/templates/macros/invalid.import.as.sameAlias.peb b/src/test/resources/templates/macros/invalid.import.as.sameAlias.peb
new file mode 100644
index 00000000..0c0b1c39
--- /dev/null
+++ b/src/test/resources/templates/macros/invalid.import.as.sameAlias.peb
@@ -0,0 +1,7 @@
+Hello
+
+{% import "templates/macros/macro.peb" as macro %}
+{% import "templates/macros/macro.peb" as macro %}
+Call 1: {{ macro.test() }}
+
+{% include "templates/macros/include.peb" %}
diff --git a/src/test/resources/templates/macros/macro.peb b/src/test/resources/templates/macros/macro.peb
index 5ce0a2aa..1870043d 100644
--- a/src/test/resources/templates/macros/macro.peb
+++ b/src/test/resources/templates/macros/macro.peb
@@ -1,3 +1,7 @@
 {% macro test() %}
 Macro-Output: {{ "test" | testfilter }}
 {% endmacro %}
+
+{% macro test2() %}
+Macro-Output: {{ "test" | testfilter }}
+{% endmacro %}