/** * Copyright 2007-2015, Kaazing Corporation. 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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 org.kaazing.k3po.lang.internal.parser; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static org.kaazing.k3po.lang.internal.RegionInfo.newParallel; import static org.kaazing.k3po.lang.internal.RegionInfo.newSequential; import static org.kaazing.k3po.lang.internal.el.ExpressionFactoryUtils.newExpressionFactory; import static org.kaazing.k3po.lang.internal.parser.ScriptParseStrategy.SCRIPT; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import javax.el.ExpressionFactory; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.BailErrorStrategy; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.NoViableAltException; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.misc.ParseCancellationException; import org.kaazing.k3po.lang.internal.RegionInfo; import org.kaazing.k3po.lang.internal.ast.AstRegion; import org.kaazing.k3po.lang.internal.ast.AstScriptNode; import org.kaazing.k3po.lang.internal.el.ExpressionContext; import org.kaazing.k3po.lang.parser.v2.RobotLexer; import org.kaazing.k3po.lang.parser.v2.RobotParser; public class ScriptParserImpl implements ScriptParser { private final ExpressionFactory factory; private final ExpressionContext context; public ScriptParserImpl() { this(newExpressionFactory(), new ExpressionContext()); } public ScriptParserImpl(ExpressionFactory factory, ExpressionContext context) { this.factory = factory; this.context = context; } public ExpressionFactory getExpressionFactory() { return factory; } public ExpressionContext getExpressionContext() { return context; } @Override public AstScriptNode parse(InputStream input) throws ScriptParseException { try { return parseWithStrategy(input, SCRIPT); } catch (Exception e) { throw new ScriptParseException(e); } } public <T extends AstRegion> T parseWithStrategy(String input, ScriptParseStrategy<T> strategy) throws ScriptParseException { return parseWithStrategy( new ByteArrayInputStream(input.getBytes(UTF_8)), strategy); } <T extends AstRegion> T parseWithStrategy(InputStream input, ScriptParseStrategy<T> strategy) throws ScriptParseException { T result = null; try { int newStart = 0; int newEnd = input.available(); ANTLRInputStream ais = new ANTLRInputStream(input); RobotLexer lexer = new RobotLexer(ais); CommonTokenStream tokens = new CommonTokenStream(lexer); final RobotParser parser = new RobotParser(tokens); parser.setErrorHandler(new BailErrorStrategy()); try { result = strategy.parse(parser, factory, context); RegionInfo regionInfo = result.getRegionInfo(); List<RegionInfo> newChildren = regionInfo.children; switch (regionInfo.kind) { case SEQUENTIAL: result.setRegionInfo(newSequential(newChildren, newStart, newEnd)); break; case PARALLEL: result.setRegionInfo(newParallel(newChildren, newStart, newEnd)); break; } } catch (ParseCancellationException pce) { Throwable cause = pce.getCause(); if (cause instanceof RecognitionException) { RecognitionException re = (RecognitionException) cause; throw createScriptParseException(parser, re); } throw pce; } catch (RecognitionException re) { throw createScriptParseException(parser, re); } } catch (IOException e) { throw new ScriptParseException(e); } return result; } private ScriptParseException createScriptParseException(RobotParser parser, RecognitionException re) { if (re instanceof NoViableAltException) { return createScriptParseException(parser, (NoViableAltException) re); } else { Token token = re.getOffendingToken(); String desc = format("line %d:%d: ", token.getLine(), token.getCharPositionInLine()); String tokenText = token.getText(); String msg = null; if (tokenText == null) { msg = "error: end of input"; } else { desc = format("%s'%s'", desc, tokenText); @SuppressWarnings("unused") String unexpectedTokenName = token.getType() != -1 ? parser .getTokenNames()[token.getType()] : parser .getTokenNames()[0]; msg = format("error: unexpected keyword '%s'", tokenText); } return new ScriptParseException(msg, re); } } private ScriptParseException createScriptParseException(RobotParser parser, NoViableAltException nvae) { String desc = String.format("line %d:%d: ", nvae.getStartToken() .getLine(), nvae.getOffendingToken().getCharPositionInLine()); String msg = String.format("%sunexpected character: '%s'", desc, escapeChar(nvae.getOffendingToken().getText().charAt(0))); return new ScriptParseException(msg); } private String escapeChar(char c) { switch (c) { case '\n': return "\\n"; case '\r': return "\\r"; case '\t': return "\\t"; default: return Character.toString(c); } } }