/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2015, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package ch.qos.logback.core.subst; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.spi.ScanException; import java.util.List; // E = TE|T // = T(E|~) // E = TEopt where Eopt = E|~ // T = LITERAL | { C } |'${' V '}' // C = E|E :- E // = E(':-'E|~) // V = E|E :- E // = E(':-'E|~) /** * Parse a token list returning a node chain. * * @author Ceki Gulcu */ public class Parser { final List<Token> tokenList; int pointer = 0; public Parser(List<Token> tokenList) { this.tokenList = tokenList; } public Node parse() throws ScanException { if (tokenList == null || tokenList.isEmpty()) return null; return E(); } private Node E() throws ScanException { Node t = T(); if (t == null) { return null; } Node eOpt = Eopt(); if (eOpt != null) { t.append(eOpt); } return t; } // Eopt = E|~ private Node Eopt() throws ScanException { Token next = peekAtCurentToken(); if (next == null) { return null; } else { return E(); } } // T = LITERAL | '${' V '}' private Node T() throws ScanException { Token t = peekAtCurentToken(); switch (t.type) { case LITERAL: advanceTokenPointer(); return makeNewLiteralNode(t.payload); case CURLY_LEFT: advanceTokenPointer(); Node innerNode = C(); Token right = peekAtCurentToken(); expectCurlyRight(right); advanceTokenPointer(); Node curlyLeft = makeNewLiteralNode(CoreConstants.LEFT_ACCOLADE); curlyLeft.append(innerNode); curlyLeft.append(makeNewLiteralNode(CoreConstants.RIGHT_ACCOLADE)); return curlyLeft; case START: advanceTokenPointer(); Node v = V(); Token w = peekAtCurentToken(); expectCurlyRight(w); advanceTokenPointer(); return v; default: return null; } } private Node makeNewLiteralNode(String s) { return new Node(Node.Type.LITERAL, s); } // V = E(':='E|~) private Node V() throws ScanException { Node e = E(); Node variable = new Node(Node.Type.VARIABLE, e); Token t = peekAtCurentToken(); if (isDefaultToken(t)) { advanceTokenPointer(); Node def = E(); variable.defaultPart = def; } return variable; } // C = E(':='E|~) private Node C() throws ScanException { Node e0 = E(); Token t = peekAtCurentToken(); if (isDefaultToken(t)) { advanceTokenPointer(); Node literal = makeNewLiteralNode(CoreConstants.DEFAULT_VALUE_SEPARATOR); e0.append(literal); Node e1 = E(); e0.append(e1); } return e0; } private boolean isDefaultToken(Token t) { return t != null && t.type == Token.Type.DEFAULT; } void advanceTokenPointer() { pointer++; } void expectNotNull(Token t, String expected) { if (t == null) { throw new IllegalArgumentException("All tokens consumed but was expecting \"" + expected + "\""); } } void expectCurlyRight(Token t) throws ScanException { expectNotNull(t, "}"); if (t.type != Token.Type.CURLY_RIGHT) { throw new ScanException("Expecting }"); } } Token peekAtCurentToken() { if (pointer < tokenList.size()) { return tokenList.get(pointer); } return null; } }