/* * Copyright (c) 2017, 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.oracle.truffle.nfi.types; import com.oracle.truffle.nfi.types.Lexer.Token; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Helper for implementors of the Truffle NFI. * * Implementors of the Truffle NFI must use {@link #parseLibraryDescriptor(java.lang.CharSequence)} * to parse the source string in {@link TruffleLanguage#parse}. The syntax of the source string is: * * <pre> * LibraryDescriptor ::= DefaultLibrary | LoadLibrary * * DefaultLibrary ::= 'default' * * LoadLibrary ::= 'load' [ '(' ident { '|' ident } ')' ] string * </pre> * * Implementors of the Truffle NFI must use {@link #parseSignature(java.lang.CharSequence)} to parse * the signature argument string of the {@code bind} method on native symbols. The syntax of a * native signature is: * * <pre> * Signature ::= '(' [ Type { ',' Type } ] [ '...' Type { ',' Type } ] ')' ':' Type * * Type ::= Signature | SimpleType | ArrayType * * SimpleType ::= ident * * ArrayType ::= '[' SimpleType ']' * </pre> */ public final class Parser { public static NativeLibraryDescriptor parseLibraryDescriptor(CharSequence source) { Parser parser = new Parser(source); NativeLibraryDescriptor ret = parser.parseLibraryDescriptor(); parser.expect(Token.EOF); return ret; } public static NativeSignature parseSignature(CharSequence source) { Parser parser = new Parser(source); NativeSignature ret = parser.parseSignature(); parser.expect(Token.EOF); return ret; } private final Lexer lexer; private Parser(CharSequence source) { lexer = new Lexer(source); } private void expect(Token token) { if (lexer.next() != token) { throw new IllegalArgumentException(String.format("unexpected token: expected '%s', but got '%s'", token.getName(), lexer.currentValue())); } } private NativeLibraryDescriptor parseLibraryDescriptor() { Token token = lexer.next(); String keyword = lexer.currentValue(); if (token == Token.IDENTIFIER) { switch (keyword) { case "load": return parseLoadLibrary(); case "default": return parseDefaultLibrary(); } } throw new IllegalArgumentException(String.format("expected 'load' or 'default', but got '%s'", keyword)); } private static NativeLibraryDescriptor parseDefaultLibrary() { return new NativeLibraryDescriptor(null, null); } private String parseIdentOrString() { if (lexer.peek() == Token.IDENTIFIER) { // support strings without quotes if they contain only identifier legal characters lexer.next(); } else { expect(Token.STRING); } return lexer.currentValue(); } private NativeLibraryDescriptor parseLoadLibrary() { List<String> flags = null; if (lexer.peek() == Token.OPENPAREN) { flags = parseFlags(); } String filename = parseIdentOrString(); return new NativeLibraryDescriptor(filename, flags); } private List<String> parseFlags() { expect(Token.OPENPAREN); ArrayList<String> flags = new ArrayList<>(); Token nextToken; do { expect(Token.IDENTIFIER); flags.add(lexer.currentValue()); nextToken = lexer.next(); } while (nextToken == Token.OR); if (nextToken != Token.CLOSEPAREN) { throw new IllegalArgumentException(String.format("unexpected token: expected '|' or ')', but got '%s'", lexer.currentValue())); } return flags; } private NativeTypeMirror parseType() { switch (lexer.peek()) { case OPENPAREN: return new NativeFunctionTypeMirror(parseSignature()); case OPENBRACKET: return parseArrayType(); case IDENTIFIER: return parseSimpleType(); default: throw new IllegalArgumentException(String.format("expected type, but got '%s'", lexer.currentValue())); } } private NativeSignature parseSignature() { expect(Token.OPENPAREN); List<NativeTypeMirror> args; int fixedArgCount = -1; if (lexer.peek() == Token.CLOSEPAREN) { lexer.next(); args = Collections.emptyList(); } else { args = new ArrayList<>(); Token nextToken; do { if (lexer.peek() == Token.ELLIPSIS) { lexer.next(); fixedArgCount = args.size(); } args.add(parseType()); nextToken = lexer.next(); } while (nextToken == Token.COMMA); if (nextToken != Token.CLOSEPAREN) { throw new IllegalArgumentException(String.format("unexpected token: expected ',' or ')', but got '%s'", lexer.currentValue())); } } expect(Token.COLON); NativeTypeMirror retType = parseType(); if (fixedArgCount >= 0) { return NativeSignature.prepareVarargs(retType, fixedArgCount, args); } else { return NativeSignature.prepare(retType, args); } } private NativeArrayTypeMirror parseArrayType() { expect(Token.OPENBRACKET); NativeSimpleTypeMirror elementType = parseSimpleType(); expect(Token.CLOSEBRACKET); return new NativeArrayTypeMirror(elementType); } private NativeSimpleTypeMirror parseSimpleType() { expect(Token.IDENTIFIER); String identifier = lexer.currentValue(); NativeSimpleType simpleType = NativeSimpleType.valueOf(identifier.toUpperCase()); return new NativeSimpleTypeMirror(simpleType); } }