All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.smithy.model.loader.IdlNodeParser Maven / Gradle / Ivy

/*
 * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package software.amazon.smithy.model.loader;

import java.util.function.Consumer;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.node.ArrayNode;
import software.amazon.smithy.model.node.BooleanNode;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.NullNode;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.node.StringNode;
import software.amazon.smithy.model.validation.Severity;
import software.amazon.smithy.model.validation.ValidationEvent;
import software.amazon.smithy.utils.Pair;

/**
 * Parses IDL nodes.
 */
final class IdlNodeParser {

    private static final String SYNTACTIC_SHAPE_ID_TARGET = "SyntacticShapeIdTarget";

    private IdlNodeParser() {}

    static Node parseNode(IdlModelParser parser) {
        return parseNode(parser, parser.currentLocation());
    }

    static Node parseNode(IdlModelParser parser, SourceLocation location) {
        char c = parser.peek();
        switch (c) {
            case '{':
                return parseObjectNode(parser, "object node", location);
            case '[':
                return parseArrayNode(parser, location);
            case '"': {
                if (peekTextBlock(parser)) {
                    return parseTextBlock(parser, location);
                } else {
                    return new StringNode(IdlTextParser.parseQuotedString(parser), location);
                }
            }
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '-':
                return parser.parseNumberNode(location);
            default: {
                return parseNodeTextWithKeywords(parser, location, ParserUtils.parseShapeId(parser));
            }
        }
    }

    static Node parseNodeTextWithKeywords(IdlModelParser parser, SourceLocation location, String text) {
        switch (text) {
            case "true":
                return new BooleanNode(true, location);
            case "false":
                return new BooleanNode(false, location);
            case "null":
                return new NullNode(location);
            default:
                // Unquoted node values syntactically are assumed to be references
                // to shapes. A lazy string node is used because the shape ID may
                // not be able to be resolved until after the entire model is loaded.
                Pair> pair = StringNode.createLazyString(text, location);
                Consumer consumer = pair.right;
                parser.addForwardReference(text, (id, typeProvider) -> {
                    if (typeProvider.apply(id) == null) {
                        parser.emit(ValidationEvent.builder()
                                .id(SYNTACTIC_SHAPE_ID_TARGET)
                                .severity(Severity.DANGER)
                                .message(String.format("Syntactic shape ID `%s` does not resolve to a valid shape ID: "
                                                       + "`%s`. Did you mean to quote this string? Are you missing a "
                                                       + "model file?", text, id))
                                .sourceLocation(location)
                                .build());
                    }
                    consumer.accept(id.toString());
                });
                return pair.left;
        }
    }

    static boolean peekTextBlock(IdlModelParser parser) {
        return parser.peek() == '"'
               && parser.peek(1) == '"'
               && parser.peek(2) == '"';
    }

    static Node parseTextBlock(IdlModelParser parser, SourceLocation location) {
        parser.expect('"');
        parser.expect('"');
        parser.expect('"');
        return new StringNode(IdlTextParser.parseQuotedTextAndTextBlock(parser, true), location);
    }

    static ObjectNode parseObjectNode(IdlModelParser parser, String parent) {
        return parseObjectNode(parser, parent, parser.currentLocation());
    }

    static ObjectNode parseObjectNode(IdlModelParser parser, String parent, SourceLocation location) {
        parser.increaseNestingLevel();
        ObjectNode.Builder builder = ObjectNode.builder()
                .sourceLocation(location);
        parser.expect('{');
        parser.ws();

        while (!parser.eof()) {
            char c = parser.peek();
            if (c == '}') {
                break;
            } else {
                SourceLocation keyLocation = parser.currentLocation();
                String key = parseNodeObjectKey(parser);
                parser.ws();
                parser.expect(':');
                if (parser.peek() == '=') {
                    throw parser.syntax("The `:=` syntax may only be used when defining inline operation input and "
                            + "output shapes.");
                }
                parser.ws();
                Node value = parseNode(parser);
                StringNode keyNode = new StringNode(key, keyLocation);
                if (builder.hasMember(key)) {
                    throw parser.syntax("Duplicate member of " + parent + ": '" + keyNode.getValue() + '\'');
                }
                builder.withMember(keyNode, value);
                parser.ws();
            }
        }

        parser.expect('}');
        parser.decreaseNestingLevel();
        return builder.build();
    }

    static String parseNodeObjectKey(IdlModelParser parser) {
        if (parser.peek() == '"') {
            return IdlTextParser.parseQuotedString(parser);
        } else {
            return ParserUtils.parseIdentifier(parser);
        }
    }

    private static ArrayNode parseArrayNode(IdlModelParser parser, SourceLocation location) {
        parser.increaseNestingLevel();
        ArrayNode.Builder builder = ArrayNode.builder()
                .sourceLocation(location);
        parser.expect('[');
        parser.ws();

        while (!parser.eof()) {
            char c = parser.peek();
            if (c == ']') {
                break;
            } else {
                builder.withValue(parseNode(parser));
                parser.ws();
            }
        }

        parser.expect(']');
        parser.decreaseNestingLevel();
        return builder.build();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy