/* * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.apple.internal.jobjc.generator.utils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import com.apple.internal.jobjc.generator.model.types.NType; import com.apple.internal.jobjc.generator.model.types.NType.NArray; import com.apple.internal.jobjc.generator.model.types.NType.NBitfield; import com.apple.internal.jobjc.generator.model.types.NType.NClass; import com.apple.internal.jobjc.generator.model.types.NType.NObject; import com.apple.internal.jobjc.generator.model.types.NType.NPointer; import com.apple.internal.jobjc.generator.model.types.NType.NPrimitive; import com.apple.internal.jobjc.generator.model.types.NType.NSelector; import com.apple.internal.jobjc.generator.model.types.NType.NStruct; import com.apple.internal.jobjc.generator.model.types.NType.NUnion; import com.apple.internal.jobjc.generator.model.types.NType.NUnknown; import com.apple.internal.jobjc.generator.model.types.NType.NVoid; /** * NTypeParser (Native Type Parser) parses type & type64 attributes from BridgeSupport. * * See Obj-C Language: Type Encodings */ public abstract class NTypeParser { // ** Parser entry point private static Map<String, NType> cached = new HashMap<String, NType>(); public static NType parseFrom(String s) { if(!cached.containsKey(s)) cached.put(s, parseFrom(new StringStream(s))); return cached.get(s); } // ** Parser driver private static List<NTypeParser> PARSERS = new ArrayList<NTypeParser>( Arrays.asList(new NBitfieldParser(), new NPrimitiveParser(), new NVoidParser(), new NPointerParser(), new NStructParser(), new NUnionParser(), new NObjectParser(), new NClassParser(), new NSelectorParser(), new NArrayParser(), new NUnknownParser(), new NSpecifierParser())); protected static NType parseFrom(StringStream ss) { if(ss.left() == 0) return null; try{ for(NTypeParser nt : PARSERS) if(nt.parsePossible(ss)) return nt.parse(ss); } catch(RuntimeException x){ throw new RuntimeException("Exception while parsing '" + ss.remainingToString() + "' from '" + ss.toString() + "'", x); } throw new RuntimeException("Found no parser for '" + ss.remainingToString() + "' from '" + ss.toString() + "'"); } // ** Methods for parsers protected abstract boolean parsePossible(StringStream ss); protected abstract NType parse(StringStream ss); // ** Individual parsers public static class NBitfieldParser extends NTypeParser{ @Override protected boolean parsePossible(StringStream ss) { return ss.left() >= 2 && ss.peekAt(0) == 'b' && Character.isDigit(ss.peekAt(1)); } @Override protected NType parse(StringStream ss) { assert parsePossible(ss); ss.eat('b'); return new NBitfield(Integer.parseInt(ss.readWhileDigits())); } } public static class NPrimitiveParser extends NTypeParser{ @Override protected boolean parsePossible(StringStream ss) { return NPrimitive.CODES.contains(ss.peek()); } @Override protected NType parse(StringStream ss) { assert parsePossible(ss); return NPrimitive.inst(ss.read()); } } public static class NVoidParser extends NTypeParser{ @Override protected boolean parsePossible(StringStream ss) { return ss.peek() == 'v'; } @Override protected NType parse(StringStream ss) { ss.eat('v'); return NVoid.inst(); } } public static class NPointerParser extends NTypeParser{ private static NPointer CHAR_PTR = new NPointer(NPrimitive.inst('C')); @Override protected boolean parsePossible(StringStream ss) { return (ss.left() >= 2 && ss.peek() == '^') || (ss.peek() == '*'); } @Override protected NType parse(StringStream ss) { if(ss.peek() == '*'){ ss.eat('*'); return CHAR_PTR; } else{ ss.eat('^'); return new NPointer(NTypeParser.parseFrom(ss)); } } } public static class NStructParser extends NTypeParser{ protected char getOpen(){ return '{'; }; protected char getClose(){ return '}'; }; @Override protected boolean parsePossible(StringStream ss) { return ss.left() >= 2 && ss.peek() == getOpen(); } @Override protected NType parse(StringStream ss) { assert parsePossible(ss); // {_NSRect= // "origin"{_NSPoint="x"f"y"f} // "size"{_NSSize="width"f"height"f}} ss.eat(getOpen()); String cname = ss.readUntilEither("=" + getClose()); List<NStruct.NField> fields = new ArrayList<NStruct.NField>(); if(ss.peek() == '='){ ss.eat('='); while(ss.peek() != getClose()){ String fname = ""; if(ss.peek() == '"'){ ss.eat('"'); fname = ss.readUntil('"'); ss.eat('"'); } NType type = NTypeParser.parseFrom(ss); fields.add(new NStruct.NField(fname, type)); } } ss.eat(getClose()); return getNew(cname, fields); } protected NType getNew(String cname, List<NStruct.NField> fields){ return new NStruct(cname, fields); } } // A Union is very much like a Struct. public static class NUnionParser extends NStructParser{ @Override protected char getOpen(){ return '('; }; @Override protected char getClose(){ return ')'; }; @Override protected NType getNew(String cname, List<NStruct.NField> fields){ return new NUnion(cname, Fp.map(NUnion.zeroOffsets, fields)); } } public static class NArrayParser extends NTypeParser{ @Override protected boolean parsePossible(StringStream ss) { return ss.peek() == '['; } @Override protected NType parse(StringStream ss) { ss.eat('['); int size = Integer.parseInt(ss.readWhileDigits()); NType type = NTypeParser.parseFrom(ss); ss.eat(']'); return new NArray(size, type); } } public static class NObjectParser extends NTypeParser{ @Override protected boolean parsePossible(StringStream ss) { return ss.peek() == '@'; } @Override protected NType parse(StringStream ss) { ss.eat('@'); return NObject.inst(); } } public static class NClassParser extends NTypeParser{ @Override protected boolean parsePossible(StringStream ss) { return ss.peek() == '#'; } @Override protected NType parse(StringStream ss) { ss.eat('#'); return NClass.inst(); } } public static class NSelectorParser extends NTypeParser{ @Override protected boolean parsePossible(StringStream ss) { return ss.peek() == ':'; } @Override protected NType parse(StringStream ss) { ss.eat(':'); return NSelector.inst(); } } public static class NUnknownParser extends NTypeParser{ @Override protected boolean parsePossible(StringStream ss) { return ss.peek() == '?'; } @Override protected NType parse(StringStream ss) { ss.eat('?'); return NUnknown.inst(); } } /** * Specifier Encoding * const r * in n * inout N * out o * bycopy O * oneway V */ public static class NSpecifierParser extends NTypeParser{ private static Collection<Character> SPECS = Arrays.asList('r', 'n', 'N', 'o', 'O', 'V'); @Override protected boolean parsePossible(StringStream ss) { return SPECS.contains(ss.peek()); } @Override protected NType parse(StringStream ss) { assert parsePossible(ss); ss.seek(); // XXX Just ignore specs for now and return the affected type. return NTypeParser.parseFrom(ss); } } }