package freeboogie.ast.gen; import java.io.IOException; import java.util.HashMap; import java.util.Map; import freeboogie.util.Err; /** * Lexer for template files. The macros and teh delimiters they use * are recognized; everything inbetween is considered as the token * `other' and will be later copied verabtim to the output. * * @author rgrig * @author reviewed by TODO */ public class TemplateLexer extends PeekStream<TemplateToken> { private CharStream stream; private Character lastChar; private StringBuilder sb; private static final Map<String, TemplateToken.Type> macros = new HashMap<String, TemplateToken.Type>(101); private static final Map<Character, TemplateToken.Type> oneCharTokens = new HashMap<Character, TemplateToken.Type>(11); private static int maxMacroLen; private static final Map<String, TemplateToken.Case> idCases = new HashMap<String, TemplateToken.Case>(7); static { macros.put("\\file", TemplateToken.Type.FILE); macros.put("\\classes", TemplateToken.Type.CLASSES); macros.put("\\if_abstract", TemplateToken.Type.IF_ABSTRACT); macros.put("\\abstract_classes", TemplateToken.Type.ABSTRACT_CLASSES); macros.put("\\normal_classes", TemplateToken.Type.NORMAL_CLASSES); macros.put("\\class_name", TemplateToken.Type.CLASS_NAME); macros.put("\\className", TemplateToken.Type.CLASS_NAME); macros.put("\\ClassName", TemplateToken.Type.CLASS_NAME); macros.put("\\CLASS_NAME", TemplateToken.Type.CLASS_NAME); macros.put("\\Classname", TemplateToken.Type.CLASS_NAME); macros.put("\\base_name", TemplateToken.Type.BASE_NAME); macros.put("\\baseName", TemplateToken.Type.BASE_NAME); macros.put("\\BaseName", TemplateToken.Type.BASE_NAME); macros.put("\\BASE_NAME", TemplateToken.Type.BASE_NAME); macros.put("\\Basename", TemplateToken.Type.BASE_NAME); macros.put("\\members", TemplateToken.Type.MEMBERS); macros.put("\\member_type", TemplateToken.Type.MEMBER_TYPE); macros.put("\\memberType", TemplateToken.Type.MEMBER_TYPE); macros.put("\\MemberType", TemplateToken.Type.MEMBER_TYPE); macros.put("\\MEMBER_TYPE", TemplateToken.Type.MEMBER_TYPE); macros.put("\\Membertype", TemplateToken.Type.MEMBER_TYPE); macros.put("\\member_name", TemplateToken.Type.MEMBER_NAME); macros.put("\\memberName", TemplateToken.Type.MEMBER_NAME); macros.put("\\MemberName", TemplateToken.Type.MEMBER_NAME); macros.put("\\MEMBER_NAME", TemplateToken.Type.MEMBER_NAME); macros.put("\\Membername", TemplateToken.Type.MEMBER_NAME); macros.put("\\if_primitive", TemplateToken.Type.IF_PRIMITIVE); macros.put("\\if_nonnull", TemplateToken.Type.IF_NONNULL); macros.put("\\if_enum", TemplateToken.Type.IF_ENUM); macros.put("\\children", TemplateToken.Type.CHILDREN); macros.put("\\primitives", TemplateToken.Type.PRIMITIVES); macros.put("\\enums", TemplateToken.Type.ENUMS); macros.put("\\enum_name", TemplateToken.Type.ENUM_NAME); macros.put("\\enumName", TemplateToken.Type.ENUM_NAME); macros.put("\\EnumName", TemplateToken.Type.ENUM_NAME); macros.put("\\ENUM_NAME", TemplateToken.Type.ENUM_NAME); macros.put("\\Enumname", TemplateToken.Type.ENUM_NAME); macros.put("\\values", TemplateToken.Type.VALUES); macros.put("\\value_name", TemplateToken.Type.VALUE_NAME); macros.put("\\valueName", TemplateToken.Type.VALUE_NAME); macros.put("\\ValueName", TemplateToken.Type.VALUE_NAME); macros.put("\\VALUE_NAME", TemplateToken.Type.VALUE_NAME); macros.put("\\Valuename", TemplateToken.Type.VALUE_NAME); macros.put("\\invariants", TemplateToken.Type.INVARIANTS); macros.put("\\inv_text", TemplateToken.Type.INV); idCases.put("\\class_name", TemplateToken.Case.LOWER_CASE); idCases.put("\\className", TemplateToken.Case.CAMEL_CASE); idCases.put("\\ClassName", TemplateToken.Case.PASCAL_CASE); idCases.put("\\CLASS_NAME", TemplateToken.Case.UPPER_CASE); idCases.put("\\Classname", TemplateToken.Case.ORIGINAL_CASE); idCases.put("\\base_name", TemplateToken.Case.LOWER_CASE); idCases.put("\\baseName", TemplateToken.Case.CAMEL_CASE); idCases.put("\\BaseName", TemplateToken.Case.PASCAL_CASE); idCases.put("\\BASE_NAME", TemplateToken.Case.UPPER_CASE); idCases.put("\\Basename", TemplateToken.Case.ORIGINAL_CASE); idCases.put("\\member_type", TemplateToken.Case.LOWER_CASE); idCases.put("\\memberType", TemplateToken.Case.CAMEL_CASE); idCases.put("\\MemberType", TemplateToken.Case.PASCAL_CASE); idCases.put("\\MEMBER_TYPE", TemplateToken.Case.UPPER_CASE); idCases.put("\\Membertype", TemplateToken.Case.ORIGINAL_CASE); idCases.put("\\member_name", TemplateToken.Case.LOWER_CASE); idCases.put("\\memberName", TemplateToken.Case.CAMEL_CASE); idCases.put("\\MemberName", TemplateToken.Case.PASCAL_CASE); idCases.put("\\MEMBER_NAME", TemplateToken.Case.UPPER_CASE); idCases.put("\\Membername", TemplateToken.Case.ORIGINAL_CASE); idCases.put("\\enum_name", TemplateToken.Case.LOWER_CASE); idCases.put("\\enumName", TemplateToken.Case.CAMEL_CASE); idCases.put("\\EnumName", TemplateToken.Case.PASCAL_CASE); idCases.put("\\ENUM_NAME", TemplateToken.Case.UPPER_CASE); idCases.put("\\Enumname", TemplateToken.Case.ORIGINAL_CASE); idCases.put("\\value_name", TemplateToken.Case.LOWER_CASE); idCases.put("\\valueName", TemplateToken.Case.CAMEL_CASE); idCases.put("\\ValueName", TemplateToken.Case.PASCAL_CASE); idCases.put("\\VALUE_NAME", TemplateToken.Case.UPPER_CASE); idCases.put("\\Valuename", TemplateToken.Case.ORIGINAL_CASE); oneCharTokens.put('[', TemplateToken.Type.LB); oneCharTokens.put(']', TemplateToken.Type.RB); oneCharTokens.put('{', TemplateToken.Type.LC); oneCharTokens.put('}', TemplateToken.Type.RC); maxMacroLen = 0; for (String s : macros.keySet()) maxMacroLen = Math.max(maxMacroLen, s.length()); } /** * Initialize a lexer. * @param stream the underlying character stream */ public TemplateLexer(CharStream stream) { super(new TokenLocation<TemplateToken>()); this.stream = stream; lastChar = null; } @Override public String getName() { return stream.getName(); } /* * This method always read ahead one character. * * @see freeboogie.ast.gen.PeekStream#read() * @see freeboogie.ast.gen.AgLexer#read() */ @Override protected TemplateToken read() throws IOException { if (lastChar == null) lastChar = stream.next(); if (lastChar == null) return null; TemplateToken.Type type = oneCharTokens.get(lastChar); TemplateToken.Case idCase = null; sb = new StringBuilder(); sb.append(lastChar); if (type != null) { lastChar = stream.next(); } else if (lastChar == '\\') { stream.mark(); // read the macro while (sb.length() <= maxMacroLen && !macros.containsKey(sb.toString())) { lastChar = stream.next(); sb.append(lastChar); } if (sb.length() > maxMacroLen) { err("Please don't use '\\' outside macro names."); sb.setLength(0); sb.append('\\'); stream.rewind(); lastChar = null; type = TemplateToken.Type.OTHER; } else if (lastChar == null) { err("The template ends abruptly while I was reading a macro: " + sb); type = TemplateToken.Type.OTHER; } else { lastChar = stream.next(); type = macros.get(sb.toString()); idCase = idCases.get(sb.toString()); } } else { // read in plain text type = TemplateToken.Type.OTHER; lastChar = stream.next(); while (lastChar != null && lastChar != '\\' && !oneCharTokens.containsKey(lastChar)) { sb.append(lastChar); lastChar = stream.next(); } } stream.eat(); return new TemplateToken(type, sb.toString(), idCase); } private void err(String e) { Err.error(getName() + stream.getLoc() + ": " + e); } }