package de.neuland.jade4j.parser; import de.neuland.jade4j.exceptions.JadeParserException; import de.neuland.jade4j.expression.ExpressionHandler; import de.neuland.jade4j.lexer.Assignment; import de.neuland.jade4j.lexer.Each; import de.neuland.jade4j.lexer.Lexer; import de.neuland.jade4j.lexer.token.*; import de.neuland.jade4j.lexer.token.AttributeList; import de.neuland.jade4j.parser.node.*; import de.neuland.jade4j.template.TemplateLoader; import de.neuland.jade4j.util.CharacterParser; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.Reader; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Parser { public static final Pattern FILE_EXTENSION_PATTERN = Pattern.compile(".*\\.\\w+$"); private Lexer lexer; private LinkedHashMap<String, BlockNode> blocks = new LinkedHashMap<String,BlockNode>(); private String[] textOnlyTags = {"script", "style"}; private Integer _spaces = null; private String basePath; private final TemplateLoader templateLoader; private ExpressionHandler expressionHandler; private Parser extending; private final String filename; private LinkedList<Parser> contexts = new LinkedList<Parser>(); private CharacterParser characterParser; private int inMixin = 0; private HashMap<String,MixinNode> mixins = new HashMap<String,MixinNode>(); private int inBlock = 0; private PathHelper pathHelper = new PathHelper(); public Parser(String filename, String basePath, TemplateLoader templateLoader, ExpressionHandler expressionHandler) throws IOException { this.filename = filename; this.basePath = basePath; this.templateLoader = templateLoader; this.expressionHandler = expressionHandler; lexer = new Lexer(filename, templateLoader,expressionHandler); characterParser = new CharacterParser(); getContexts().push(this); } public Parser(String src, String filename, String basePath, TemplateLoader templateLoader, ExpressionHandler expressionHandler) throws IOException { this.filename = filename; this.basePath = basePath; this.templateLoader = templateLoader; this.expressionHandler = expressionHandler; lexer = new Lexer(src,filename, templateLoader,expressionHandler); characterParser = new CharacterParser(); getContexts().push(this); } public Node parse() { BlockNode block = new BlockNode(); block.setLineNumber(lexer.getLineno()); block.setFileName(filename); while (!(peek() instanceof Eos)) { if (peek() instanceof Newline) { advance(); } else { Node expr = parseExpr(); if (expr != null) { block.push(expr); } } } if (extending != null) { getContexts().push(extending); Node rootNode = extending.parse(); getContexts().pop(); // hoist mixins Set<String> keySet = this.mixins.keySet(); for (String name : keySet) { rootNode.getNodes().push(this.mixins.get(name)); } return rootNode; } return block; } private Node parseExpr() { Token token = peek(); if (token instanceof Tag) { return parseTag(); } if (token instanceof Mixin) { return parseMixin(); } if (token instanceof Block) { return parseBlock(); } if (token instanceof MixinBlock) { return parseMixinBlock(); } if (token instanceof CaseToken) { return parseCase(); } if (token instanceof ExtendsToken) { return parseExtends(); } if (token instanceof Include) { return parseInclude(); } if (token instanceof Doctype) { return parseDoctype(); } if (token instanceof Filter) { return parseFilter(); } if (token instanceof Comment) { return parseComment(); } if (token instanceof Text) { return parseText(); } if (token instanceof Each) { return parseEach(); } // if (token instanceof Code) { // return parseCode(); // } if (token instanceof Expression) { return parseCode(); } if (token instanceof BlockCode) { return parseBlockCode(); } if (token instanceof Call) { return parseCall(); } if (token instanceof Interpolation) { return parseInterpolation(); } if (token instanceof Yield) { return parseYield(); } if (token instanceof CssClass || token instanceof CssId) { return parseCssClassOrId(); } if (token instanceof While) { return parseWhile(); } if (token instanceof If) { return parseConditional(); } if (token instanceof Assignment) { return parseAssignment(); } throw new JadeParserException(filename, lexer.getLineno(), templateLoader, token); } /** * block code */ private Node parseBlockCode(){ Token tok = this.expect(BlockCode.class); ExpressionNode node; Token body = this.peek(); String text; if (body instanceof PipelessText) { this.advance(); text = StringUtils.join(body.getValues(),"\n"); } else { text = ""; } node = new ExpressionNode(); node.setValue(text); node.setLineNumber(tok.getLineNumber()); return node; } private Node parseComment() { Token token = expect(Comment.class); Node block = this.parseTextBlock(); if (block != null ) { BlockCommentNode node = new BlockCommentNode(); node.setBlock(block); node.setBuffered(token.isBuffer()); node.setLineNumber(token.getLineNumber()); node.setFileName(filename); node.setValue(token.getValue()); return node; } else { CommentNode node = new CommentNode(); node.setBuffered(token.isBuffer()); node.setLineNumber(token.getLineNumber()); node.setFileName(filename); node.setValue(token.getValue()); return node; } } private Node parseMixin() { Mixin mixinToken = (Mixin) expect(Mixin.class); MixinNode node = new MixinNode(); node.setName(mixinToken.getValue()); node.setLineNumber(mixinToken.getLineNumber()); node.setFileName(filename); if (StringUtils.isNotBlank(mixinToken.getArguments())) { node.setArguments(mixinToken.getArguments()); } List<String> args = node.getArguments(); String rest; if (args.size() > 0) { Matcher matcher = Pattern.compile("^\\.\\.\\.").matcher(args.get(args.size() - 1).trim()); if (matcher.find(0)) { rest = args.remove(args.size() - 1).trim().replaceAll("^\\.\\.\\.", ""); node.setRest(rest); } } if (peek() instanceof Indent) { this.inMixin++; node.setBlock(block()); node.setCall(false); this.mixins.put(mixinToken.getValue(),node); this.inMixin--; return node; }else{ node.setCall(true); return node; } } private Node parseCall() { Token token = expect(Call.class); Call callToken = (Call) token; MixinNode mixin = new MixinNode(); mixin.setBlock(new BlockNode()); mixin.setName(callToken.getValue()); mixin.setLineNumber(callToken.getLineNumber()); mixin.setFileName(filename); mixin.setCall(true); if (StringUtils.isNotBlank(callToken.getArguments())) { mixin.setArguments(callToken.getArguments()); } this.tag(mixin); if(mixin.hasCodeNode()) { mixin.getBlock().push(mixin.getCodeNode()); mixin.setCodeNode(null); } if(mixin.hasBlock() && mixin.getBlock().getNodes().isEmpty()) mixin.setBlock(null); return mixin; } private Node parseCssClassOrId() { Token tok = advance(); Tag div = new Tag("div", line()); lexer.defer(div); lexer.defer(tok); return parseExpr(); } private Node parseBlock() { Token token = expect(Block.class); Block block = (Block) token; String mode = block.getMode(); String name = block.getValue().trim(); this.inBlock++; BlockNode blockNode; if (peek() instanceof Indent) { blockNode = block(); } else { blockNode = new BlockNode(); blockNode.setLineNumber(block.getLineNumber()); blockNode.setFileName(filename); LiteralNode node = new LiteralNode(); node.setValue(""); blockNode.push(node); } this.inBlock--; blockNode.setName(name); blockNode.setLineNumber(line()); BlockNode prev; if(this.blocks.get(name)==null) prev = new BlockNode(); else prev = this.blocks.get(name); if ("replace".equals(prev.getMode())) { this.blocks.put(name, prev); return prev; } LinkedList<Node> allNodes = new LinkedList<Node>(); allNodes.addAll(prev.getPrepended()); allNodes.addAll(blockNode.getNodes()); allNodes.addAll(prev.getAppended()); //ok if ("append".equals(mode)) { LinkedList<Node> appendedNodes = new LinkedList<Node>(); if(prev.getParser() == this){ appendedNodes.addAll(prev.getAppended()); appendedNodes.addAll(blockNode.getNodes()); }else{ appendedNodes.addAll(blockNode.getNodes()); appendedNodes.addAll(prev.getAppended()); } prev.setAppended(appendedNodes); } else if ("prepend".equals(mode)) { LinkedList<Node> prependedNodes = new LinkedList<Node>(); if(prev.getParser() == this){ prependedNodes.addAll(blockNode.getNodes()); prependedNodes.addAll(prev.getPrepended()); }else{ prependedNodes.addAll(prev.getPrepended()); prependedNodes.addAll(blockNode.getNodes()); } prev.setPrepended(prependedNodes); } blockNode.setNodes(allNodes); blockNode.setAppended(prev.getAppended()); blockNode.setPrepended(prev.getPrepended()); blockNode.setMode(mode); blockNode.setParser(this); blockNode.setSubBlock(this.inBlock>0); blocks.put(name, blockNode); return blockNode; } private Node parseMixinBlock(){ Token tok = expect(MixinBlock.class); if(this.inMixin == 0){ throw new JadeParserException(filename, lexer.getLineno(), templateLoader, "Anonymous blocks are not allowed unless they are part of a mixin."); } return new MixinBlockNode(); } private Node parseInclude() { Token token = expect(Include.class); Include includeToken = (Include) token; String templateName = includeToken.getValue().trim(); String path = pathHelper.resolvePath(filename,templateName,basePath); try { if (includeToken.getFilter() != null) { Reader reader = templateLoader.getReader(path); FilterNode node = new FilterNode(); node.setValue(includeToken.getFilter()); node.setLineNumber(line()); node.setFileName(filename); TextNode text = new TextNode(); text.setValue(IOUtils.toString(reader)); BlockNode block = new BlockNode(); LinkedList<Node> nodes = new LinkedList<Node>(); nodes.add(text); block.setNodes(nodes); if (block != null) node.setTextBlock(block); else { node.setTextBlock(new BlockNode()); } return node; } } catch (IOException e) { throw new JadeParserException(filename, lexer.getLineno(), templateLoader, "the included file [" + templateName + "] could not be opened\n" + e.getMessage()); } // non-jade String extension = FilenameUtils.getExtension(path); if (!"jade".equals(extension)) { try { Reader reader = templateLoader.getReader(path); LiteralNode node = new LiteralNode(); node.setLineNumber(lexer.getLineno()); node.setFileName(filename); node.setValue(IOUtils.toString(reader)); return node; } catch (IOException e) { throw new JadeParserException(filename, lexer.getLineno(), templateLoader, "the included file [" + templateName + "] could not be opened\n" + e.getMessage()); } } Parser parser = createParser(templateName); parser.setBlocks(new LinkedHashMap<String,BlockNode>(blocks)); parser.setMixins(mixins); contexts.push(parser); Node ast = parser.parse(); contexts.pop(); ast.setFileName(path); if (peek() instanceof Indent && ast != null) { ((BlockNode) ast).getIncludeBlock().push(block()); } return ast; } private Node parseExtends() { Token token = expect(ExtendsToken.class); ExtendsToken extendsToken = (ExtendsToken) token; String templateName = extendsToken.getValue().trim(); Parser parser = createParser(templateName); parser.setBlocks(blocks); parser.setContexts(contexts); extending = parser; LiteralNode node = new LiteralNode(); node.setValue(""); return node; } private Parser createParser(String templateName) { templateName = ensureJadeExtension(templateName); try { return new Parser(pathHelper.resolvePath(filename,templateName, basePath), basePath, templateLoader, expressionHandler); } catch (IOException e) { throw new JadeParserException(filename, lexer.getLineno(), templateLoader, "the template [" + templateName + "] could not be opened\n" + e.getMessage()); } } private String ensureJadeExtension(String templateName) { if (!"jade".equals(FilenameUtils.getExtension(templateName))) { return templateName + ".jade"; } return templateName; } private BlockNode parseYield() { advance(); BlockNode block = new BlockNode(); block.setLineNumber(lexer.getLineno()); block.setFileName(filename); block.setYield(true); return block; } private Node parseInterpolation() { Token token = advance(); String name = token.getValue(); TagNode tagNode = new TagNode(); tagNode.setLineNumber(lexer.getLineno()); tagNode.setFileName(filename); tagNode.setName(name); tagNode.setBuffer(true); return this.tag(tagNode); } private Node blockExpansion() { if (peek() instanceof Colon) { Token token = expect(Colon.class); Colon colon = (Colon) token; BlockNode block = new BlockNode(); block.setLineNumber(colon.getLineNumber()); block.setFileName(filename); block.getNodes().add(parseExpr()); return block; } return block(); } private BlockNode block() { BlockNode block = new BlockNode(); block.setLineNumber(lexer.getLineno()); block.setFileName(filename); expect(Indent.class); while (!(peek() instanceof Outdent)) { if (peek() instanceof Newline) { advance(); } else { Node parseExpr = this.parseExpr(); parseExpr.setFileName(filename); if (parseExpr != null) { block.push(parseExpr); } } } expect(Outdent.class); return block; } private List<CaseConditionNode> whenBlock() { expect(Indent.class); List<CaseConditionNode> caseConditionalNodes = new LinkedList<CaseConditionNode>(); while (!(peek() instanceof Outdent) && !(peek() instanceof Eos)) { if (peek() instanceof Newline) { advance(); } else { caseConditionalNodes.add(this.parseCaseCondition()); } } if (peek() instanceof Outdent) { expect(Outdent.class); } return caseConditionalNodes; } private Node parseText() { Token tok = expect(Text.class); Node[] tokens = this.parseInlineTagsInText(tok.getValue()); if (tokens.length == 1) return tokens[0]; BlockNode node = new BlockNode(); for (int i = 0; i < tokens.length; i++) { node.push(tokens[i]); } node.setValue(tok.getValue()); node.setLineNumber(tok.getLineNumber()); node.setFileName(filename); return node; } private Node parseEach() { Token token = expect(Each.class); Each eachToken = (Each) token; EachNode node = new EachNode(); node.setValue(eachToken.getValue()); node.setKey(eachToken.getKey()); node.setCode(eachToken.getCode()); node.setLineNumber(eachToken.getLineNumber()); node.setFileName(filename); node.setBlock(block()); if (peek() instanceof Else) { advance(); node.setElseNode(block()); } return node; } private Node parseWhile() { Token token = expect(While.class); While whileToken = (While) token; WhileNode node = new WhileNode(); node.setValue(whileToken.getValue()); node.setLineNumber(whileToken.getLineNumber()); node.setFileName(filename); BlockNode block = block(); if(block!=null) node.setBlock(block); else node.setBlock(new BlockNode()); return node; } private Node parseAssignment() { Token token = expect(Assignment.class); Token assignmentToken = (Assignment) token; Node node = new AssigmentNode(); node.setName(assignmentToken.getName()); node.setValue(assignmentToken.getValue()); node.setLineNumber(assignmentToken.getLineNumber()); node.setFileName(filename); return node; } private Node parseTag() { Token token = advance(); String name = token.getValue(); TagNode tagNode = new TagNode(); tagNode.setLineNumber(lexer.getLineno()); tagNode.setFileName(filename); tagNode.setName(name); tagNode.setValue(name); tagNode.setSelfClosing(token.isSelfClosing()); return this.tag(tagNode); } private Node tag(AttrsNode tagNode){ // ast-filter look-ahead boolean seenAttrs = false; while (true) { Token incomingToken = peek(); if (incomingToken instanceof CssId) { Token tok = advance(); tagNode.setAttribute("id", tok.getValue(),false); continue; } else if (incomingToken instanceof CssClass) { Token tok = advance(); tagNode.setAttribute("class", tok.getValue(),false); continue; } else if (incomingToken instanceof AttributeList) { if (seenAttrs) { throw new JadeParserException(filename,line(),templateLoader,this.filename + ", line " + this.peek().getLineNumber() + ":\nYou should not have jade tags with multiple attributes."); //console.warn(this.filename + ', line ' + this.peek().line + ':\nYou should not have jade tags with multiple attributes.'); } seenAttrs = true; AttributeList tok = (AttributeList) advance(); List<Attribute> attrs = tok.getAttributes(); tagNode.setSelfClosing(tok.isSelfClosing()); for (Attribute attr : attrs) { String name = attr.getName(); Object value = attr.getValue(); tagNode.setAttribute(name, value, attr.isEscaped()); } continue; } else if (incomingToken instanceof AttributesBlock) { Token tok = this.advance(); tagNode.addAttributes(tok.getValue()); } else { break; } } // check immediate '.' boolean dot = false; if (peek() instanceof Dot) { dot = true; tagNode.setTextOnly(true); advance(); } // (text | code | ':')? if (peek() instanceof Text) { tagNode.getBlock().push(parseText()); } else if (peek() instanceof Expression) { tagNode.setCodeNode(parseCode()); } else if (peek() instanceof Colon) { Token next = advance(); BlockNode block = new BlockNode(); block.setLineNumber(next.getLineNumber()); block.setFileName(filename); block.push(parseExpr()); tagNode.setBlock(block); } // newline* while (peek() instanceof Newline) { advance(); } if (tagNode.isTextOnly()) { Node block = this.parseTextBlock(); if(block == null) block = new BlockNode(); tagNode.setBlock(block); }else if(peek() instanceof Indent){ BlockNode block = block(); // if(!tagNode.hasBlock()) // tagNode.setBlock(new BlockNode()); for (int i = 0, len = block.getNodes().size(); i < len; ++i) { tagNode.getBlock().push(block.getNodes().get(i)); } } // else{ // if (Arrays.asList(textOnlyTags).contains(tagNode.getName())) { // tagNode.setTextOnly(true); // } // } // // // script special-case // if ("script".equals(tagNode.getName())) { // String type = tagNode.getAttribute("type"); // if (!dot && StringUtils.isNotBlank(type)) { // String cleanType = type.replaceAll("^['\"]|['\"]$", ""); // if (!"text/javascript".equals(cleanType)) { // tagNode.setTextOnly(false); // } // } // } // // if (peek() instanceof Indent) { // if (tagNode.isTextOnly()) { // lexer.setPipeless(true); // tagNode.setTextNode(parseTextBlock()); // lexer.setPipeless(false); // } else { // Node blockNode = block(); // if (tagNode.hasBlock()) { // tagNode.getBlock().getNodes().addAll(blockNode.getNodes()); // } else { // tagNode.setBlock(blockNode); // } // } // } return tagNode; } private Node[] parseInlineTagsInText(String str) { int line = this.line(); Matcher matcher = Pattern.compile("(\\\\)?#\\[((?:.|\\n)*)$").matcher(str); TextNode text = new TextNode(); text.setLineNumber(line); text.setFileName(filename); if (matcher.find(0) && matcher.groupCount()>1) { if (matcher.group(1) != null) { // escape text.setValue(str.substring(0, matcher.start()) + "#[");//Not sure if Matcher.end() is correct Node[] rest = this.parseInlineTagsInText(matcher.group(2)); if (rest[0] instanceof TextNode) { text.setValue(text.getValue() + rest[0].getValue()); rest = ArrayUtils.remove(rest,0); } Node[] textNodes = {text}; return ArrayUtils.addAll(textNodes, rest); } else { text.setValue(str.substring(0, matcher.start()));//Not sure if Matcher.end() is correct Node[] textNodes = {text}; Node[] buffer = textNodes; String rest = matcher.group(2); CharacterParser.Match range = null; try { range = characterParser.parseMax(rest); } catch (CharacterParser.SyntaxError syntaxError) { throw new JadeParserException(this.filename,line,templateLoader," See "+matcher.group(0)); } Parser inner = null; try { inner = new Parser(range.getSrc(), this.filename,this.basePath, this.templateLoader,this.expressionHandler); //Need to be reviewed } catch (IOException e) { throw new JadeParserException(this.filename,line,templateLoader,"Could not parse text"); } buffer = ArrayUtils.add(buffer,inner.parse()); return ArrayUtils.addAll(buffer, this.parseInlineTagsInText(rest.substring(range.getEnd() + 1))); } } else { text.setValue(str); Node[] textNodes = {text}; return textNodes; } } private Node parseTextBlock() { BlockNode blockNode = new BlockNode(); blockNode.setLineNumber(line()); blockNode.setFileName(filename); Token body = peek(); if(!(body instanceof PipelessText)){ return null; } this.advance(); ArrayList<String> values = body.getValues(); Node[] textNodes = {}; for (String value : values) { textNodes = ArrayUtils.addAll(textNodes,parseInlineTagsInText(value)); } blockNode.setNodes(new LinkedList<Node>(Arrays.asList(textNodes))); return blockNode; } private Node parseConditional() { If conditionalToken = (If) expect(If.class); ConditionalNode conditional = new ConditionalNode(); conditional.setLineNumber(conditionalToken.getLineNumber()); conditional.setFileName(filename); List<IfConditionNode> conditions = conditional.getConditions(); IfConditionNode main = new IfConditionNode(conditionalToken.getValue(), conditionalToken.getLineNumber()); main.setInverse(conditionalToken.isInverseCondition()); main.setBlock(block()); conditions.add(main); while (peek() instanceof ElseIf) { ElseIf token = (ElseIf) expect(ElseIf.class); IfConditionNode elseIf = new IfConditionNode(token.getValue(), token.getLineNumber()); elseIf.setBlock(block()); conditions.add(elseIf); } if (peek() instanceof Else) { Else token = (Else) expect(Else.class); IfConditionNode elseNode = new IfConditionNode(null, token.getLineNumber()); elseNode.setDefault(true); elseNode.setBlock(block()); conditions.add(elseNode); } return conditional; } // var block = new nodes.Block; // block.line = this.line(); // block.filename = this.filename; // this.expect('indent'); // while ('outdent' != this.peek().type) { // switch (this.peek().type) { // case 'comment': // case 'newline': // this.advance(); // break; // case 'when': // block.push(this.parseWhen()); // break; // case 'default': // block.push(this.parseDefault()); // break; // default: // throw new Error('Unexpected token "' + this.peek().type // + '", expected "when", "default" or "newline"'); // } // } // this.expect('outdent'); // // node.block = block; // // return node; private Node parseBlockExpansion(){ if (this.peek() instanceof Colon) { this.advance(); BlockNode blockNode = new BlockNode(); blockNode.push(this.parseExpr()); return blockNode; } else { return this.block(); } } private Node parseCase() { String val = expect(CaseToken.class).getValue(); Node node = new CaseNode(); node.setValue(val); node.setLineNumber(line()); Node block = new BlockNode(); block.setLineNumber(line()); block.setFileName(filename); expect(Indent.class); while (!(peek() instanceof Outdent)) { if (peek() instanceof Comment) { advance(); } else if (peek() instanceof Newline) { advance(); } else if (peek() instanceof When) { block.push(parseWhen()); } else if (peek() instanceof Default) { block.push(parseDefault()); } else { throw new JadeParserException(filename,lexer.getLineno(),templateLoader,"Unexpected token \"" + this.peek() + "\", expected \"when\", \"default\" or \"newline\""); } } expect(Outdent.class); node.setBlock(block); return node; } /** * when */ private Node parseWhen(){ String val = this.expect(When.class).getValue(); CaseNode.When when = new CaseNode.When(); when.setValue(val); if (!(this.peek() instanceof Newline)) { when.setBlock(this.parseBlockExpansion()); } return when; } /** * default */ private Node parseDefault(){ expect(Default.class); Node when = new CaseNode.When(); when.setValue("default"); when.setBlock(this.parseBlockExpansion()); return when; } private CaseConditionNode parseCaseCondition() { CaseConditionNode node = new CaseConditionNode(); Token token = null; if (peek() instanceof When) { token = expect(When.class); } else { token = expect(Default.class); node.setDefault(true); } node.setLineNumber(token.getLineNumber()); node.setFileName(filename); node.setValue(token.getValue()); node.setBlock(blockExpansion()); return node; } private Node parseCode() { Token token = expect(Expression.class); Expression expressionToken = (Expression) token; ExpressionNode codeNode = new ExpressionNode(); codeNode.setValue(expressionToken.getValue()); codeNode.setBuffer(expressionToken.isBuffer()); codeNode.setEscape(expressionToken.isEscape()); codeNode.setLineNumber(expressionToken.getLineNumber()); codeNode.setFileName(filename); boolean block = false; // int i = 1; // while (lookahead(i) != null && lookahead(i) instanceof Newline) // ++i; block = peek() instanceof Indent; if (block) { codeNode.setBlock((BlockNode) block()); } return codeNode; } private Node parseDoctype() { Token token = expect(Doctype.class); Doctype doctype = (Doctype) token; DoctypeNode doctypeNode = new DoctypeNode(); doctypeNode.setValue(doctype.getValue()); return doctypeNode; } // var tok = this.expect('code') // , node = new nodes.Code(tok.val, tok.buffer, tok.escape) // , block // , i = 1; // node.line = this.line(); // while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i; // block = 'indent' == this.lookahead(i).type; // if (block) { // this.skip(i-1); // node.block = this.block(); // } // return node; private Node parseFilter() { Token token = expect(Filter.class); Filter filterToken = (Filter) token; AttributeList attr = (AttributeList) accept(AttributeList.class); lexer.setPipeless(true); Node tNode = parseTextBlock(); lexer.setPipeless(false); FilterNode node = new FilterNode(); node.setValue(filterToken.getValue()); node.setLineNumber(line()); node.setFileName(filename); if(tNode!=null) node.setTextBlock(tNode); else{ node.setTextBlock(new BlockNode()); } if (attr != null) { node.setAttributes(convertToNodeAttributes(attr)); } return node; } private Node parseASTFilter() { Token token = expect(Filter.class); Filter filterToken = (Filter) token; AttributeList attr = (AttributeList) accept(AttributeList.class); token = expect(Colon.class); FilterNode node = new FilterNode(); node.setValue(filterToken.getValue()); node.setBlock(block()); node.setLineNumber(line()); node.setFileName(filename); node.setAttributes(convertToNodeAttributes(attr)); return node; } private List<Attr> convertToNodeAttributes(AttributeList attr) { List<Attribute> attributes = attr.getAttributes(); List<Attr> attributeNodes = new LinkedList<Attr>(); for (Attribute attribute : attributes) { attributeNodes.add(new Attr(attribute.getName(),attribute.getValue(),attribute.isEscaped())); } return attributeNodes; } private Token lookahead(int i) { return lexer.lookahead(i); } private Token peek() { return lookahead(1); } private void skip(int n) { while (n > 0) { lexer.advance(); n = n - 1; } } private Token advance() { return lexer.advance(); } @SuppressWarnings("rawtypes") private Token accept(Class clazz) { if (this.peek().getClass().equals(clazz)) { return lexer.advance(); } return null; } private int line() { return lexer.getLineno(); } @SuppressWarnings("rawtypes") private Token expect(Class expectedTokenClass) { Token t = this.peek(); if (t.getClass().equals(expectedTokenClass)) { return advance(); } else { throw new JadeParserException(filename, lexer.getLineno(), templateLoader, expectedTokenClass, t.getClass()); } } public Map<String, BlockNode> getBlocks() { return blocks; } public void setBlocks(LinkedHashMap<String, BlockNode> blocks) { this.blocks = blocks; } public LinkedList<Parser> getContexts() { return contexts; } public void setContexts(LinkedList<Parser> contexts) { this.contexts = contexts; } public void setMixins(HashMap mixins) { this.mixins = mixins; } }