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

com.io7m.jsx.parser.JSXParser Maven / Gradle / Ivy

There is a newer version: 0.7.0
Show newest version
/*
 * Copyright © 2016  http://io7m.com
 * 
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jsx.parser;

import com.io7m.jlexing.core.LexicalPositionType;
import com.io7m.jnull.NullCheck;
import com.io7m.jsx.SExpressionQuotedStringType;
import com.io7m.jsx.SExpressionType;
import com.io7m.jsx.lexer.JSXLexerException;
import com.io7m.jsx.lexer.JSXLexerType;
import com.io7m.jsx.tokens.TokenEOF;
import com.io7m.jsx.tokens.TokenLeftParenthesis;
import com.io7m.jsx.tokens.TokenLeftSquare;
import com.io7m.jsx.tokens.TokenQuotedString;
import com.io7m.jsx.tokens.TokenRightParenthesis;
import com.io7m.jsx.tokens.TokenRightSquare;
import com.io7m.jsx.tokens.TokenSymbol;
import com.io7m.jsx.tokens.TokenType;
import com.io7m.junreachable.UnreachableCodeException;

import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * The default implementation of the {@link JSXParserType} type.
 */

public final class JSXParser implements JSXParserType
{
  private final JSXParserConfiguration config;
  private final JSXLexerType           lexer;

  private JSXParser(
    final JSXParserConfiguration in_config,
    final JSXLexerType in_lexer)
  {
    this.config = NullCheck.notNull(in_config);
    this.lexer = NullCheck.notNull(in_lexer);
  }

  private static SExpressionQuotedStringType completeQuotedString(
    final JSXParserConfiguration c,
    final TokenQuotedString t)
  {
    return new PQuotedString(t.getText(), JSXParser.getTokenLexical(c, t));
  }

  private static SExpressionType completeSymbol(
    final JSXParserConfiguration c,
    final TokenSymbol t)
  {
    final Optional> lex =
      JSXParser.getTokenLexical(c, t);
    return new PSymbol(t.getText(), lex);
  }

  private static Optional> getTokenLexical(
    final JSXParserConfiguration c,
    final TokenType t)
  {
    final Optional> lex;
    if (!c.preserveLexicalInformation()) {
      lex = Optional.empty();
    } else {
      lex = Optional.of(t.getLexicalInformation());
    }
    return lex;
  }

  private static JSXParserGrammarException errorUnexpectedEOF(
    final TokenEOF t)
  {
    return new JSXParserGrammarException(
      t.getLexicalInformation(), "Unexpected EOF during list parsing");
  }

  private static JSXParserGrammarException errorUnexpectedRightParen(
    final TokenRightParenthesis t)
  {
    return new JSXParserGrammarException(
      t.getLexicalInformation(), "Unbalanced parentheses (unexpected ')')");
  }

  private static JSXParserGrammarException
  errorUnexpectedRightParenWantedSquare(
    final TokenRightParenthesis t)
  {
    return new JSXParserGrammarException(
      t.getLexicalInformation(),
      "Attempted to end a list started with '[' with ')' - unbalanced "
      + "round/square brackets");
  }

  private static JSXParserGrammarException errorUnexpectedRightSquare(
    final TokenRightSquare t)
  {
    return new JSXParserGrammarException(
      t.getLexicalInformation(), "Unbalanced parentheses (unexpected ']')");
  }

  private static JSXParserGrammarException
  errorUnexpectedRightSquareWantedParens(
    final TokenRightSquare t)
  {
    return new JSXParserGrammarException(
      t.getLexicalInformation(),
      "Attempted to end a list started with '(' with ']' - unbalanced "
      + "round/square brackets");
  }

  /**
   * Construct a new parser.
   *
   * @param pc  The parser configuration
   * @param lex A lexer
   *
   * @return A new parser
   */

  public static JSXParserType newParser(
    final JSXParserConfiguration pc,
    final JSXLexerType lex)
  {
    return new JSXParser(pc, lex);
  }

  private static SExpressionType parseExpressionPeeked(
    final JSXParserConfiguration c,
    final JSXLexerType lexer,
    final TokenType peek)
    throws JSXLexerException, IOException, JSXParserGrammarException
  {
    if (peek instanceof TokenLeftParenthesis) {
      return JSXParser.parseListParens(c, lexer, (TokenLeftParenthesis) peek);
    }
    if (peek instanceof TokenLeftSquare) {
      return JSXParser.parseListSquares(c, lexer, (TokenLeftSquare) peek);
    }
    if (peek instanceof TokenRightSquare) {
      throw JSXParser.errorUnexpectedRightSquare((TokenRightSquare) peek);
    }
    if (peek instanceof TokenRightParenthesis) {
      throw JSXParser.errorUnexpectedRightParen((TokenRightParenthesis) peek);
    }
    if (peek instanceof TokenQuotedString) {
      return JSXParser.completeQuotedString(c, (TokenQuotedString) peek);
    }
    if (peek instanceof TokenSymbol) {
      return JSXParser.completeSymbol(c, (TokenSymbol) peek);
    }
    if (peek instanceof TokenEOF) {
      throw JSXParser.errorUnexpectedEOF((TokenEOF) peek);
    }

    throw new UnreachableCodeException();
  }

  private static SExpressionType parseListParens(
    final JSXParserConfiguration c,
    final JSXLexerType lexer,
    final TokenLeftParenthesis peek)
    throws JSXLexerException, IOException, JSXParserGrammarException
  {
    final PList xs = new PList(
      new ArrayList<>(16), JSXParser.getTokenLexical(c, peek), false);

    while (true) {
      final TokenType t = lexer.token();
      if (t instanceof TokenEOF) {
        throw JSXParser.errorUnexpectedEOF((TokenEOF) t);
      }
      if (t instanceof TokenRightParenthesis) {
        return xs;
      }
      if (t instanceof TokenRightSquare) {
        throw JSXParser.errorUnexpectedRightSquareWantedParens(
          (TokenRightSquare) t);
      }
      xs.add(JSXParser.parseExpressionPeeked(c, lexer, t));
    }
  }

  private static SExpressionType parseListSquares(
    final JSXParserConfiguration c,
    final JSXLexerType lexer,
    final TokenLeftSquare peek)
    throws JSXLexerException, IOException, JSXParserGrammarException
  {
    final PList xs = new PList(
      new ArrayList<>(16), JSXParser.getTokenLexical(c, peek), true);

    while (true) {
      final TokenType t = lexer.token();
      if (t instanceof TokenEOF) {
        throw JSXParser.errorUnexpectedEOF((TokenEOF) t);
      }
      if (t instanceof TokenRightParenthesis) {
        throw JSXParser.errorUnexpectedRightParenWantedSquare(
          (TokenRightParenthesis) t);
      }
      if (t instanceof TokenRightSquare) {
        return xs;
      }
      xs.add(JSXParser.parseExpressionPeeked(c, lexer, t));
    }
  }

  @Override public SExpressionType parseExpression()
    throws JSXParserException, IOException
  {
    try {
      final TokenType peek = this.lexer.token();
      return JSXParser.parseExpressionPeeked(this.config, this.lexer, peek);
    } catch (final JSXLexerException e) {
      throw new JSXParserLexicalException(e);
    }
  }

  @Override public Optional parseExpressionOrEOF()
    throws JSXParserException, IOException
  {
    try {
      final TokenType peek = this.lexer.token();
      if (peek instanceof TokenEOF) {
        return Optional.empty();
      }

      return Optional.of(
        JSXParser.parseExpressionPeeked(
          this.config, this.lexer, peek));
    } catch (final JSXLexerException e) {
      throw new JSXParserLexicalException(e);
    }
  }

  @Override public List parseExpressions()
    throws JSXParserException, IOException
  {
    try {
      final List xs = new ArrayList<>(64);
      while (true) {
        final TokenType peek = this.lexer.token();
        if (peek instanceof TokenEOF) {
          return xs;
        }
        xs.add(JSXParser.parseExpressionPeeked(this.config, this.lexer, peek));
      }
    } catch (final JSXLexerException e) {
      throw new JSXParserLexicalException(e);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy