beaver.ParsingTables Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of soot Show documentation
Show all versions of soot Show documentation
A Java Optimization Framework
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This file is part of Beaver Parser Generator. *
* Copyright (C) 2003,2004 Alexander Demenchuk . *
* All rights reserved. *
* See the file "LICENSE" for the terms and conditions for copying, *
* distribution and modification of Beaver. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package beaver;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.InflaterInputStream;
/**
* Parsing Tables
*/
public final class ParsingTables
{
/** A table with all actions */
private final short[] actions;
/**
* A table containing the lookahead for each entry in "actions" table.
* Used to detect "collisions".
*/
final short[] lookaheads;
/**
* For each state, the offset into "actions" table that is used to find action for a terminal
* that has been fetched from the scanner.
*/
final int[] actn_offsets;
/**
* For each state, the offset into "actions" table that is used to find a next parser's state
* using a nonterminal that has been created by a reduced production.
*/
private final int[] goto_offsets;
/** Default action for each state */
private final short[] default_actions;
/**
* A table with encoded production information.
*
* Each slot in this table is a "structure":
*
* short lhs_symbol_id ; // Symbol on the left-hand side of the production
* short rhs_length ; // Number of right-hand side symbols in the production
*
* where lhs_symbol_id uses high 16 bit of this structure, and rhs_length - lower 16 bits
*/
final int[] rule_infos;
/** ID of the "error" nonterminal */
final short error_symbol_id;
/** Indicates whether action tables were compressed. */
final boolean compressed;
/** Number of terminal symbols. */
final int n_term;
/**
* Ensures that parser tables are loaded.
*
* @param impl_class class of the instance of the Parser
*/
public ParsingTables(Class impl_class)
{
this(getSpecAsResourceStream(impl_class));
}
public ParsingTables(String spec)
{
this(new ByteArrayInputStream(decode(spec)));
}
private ParsingTables(InputStream in)
{
try
{
DataInputStream data = new DataInputStream(new InflaterInputStream(in));
try
{
int len = data.readInt();
actions = new short[len];
for (int i = 0; i < len; i++)
{
actions[i] = data.readShort();
}
lookaheads = new short[len];
for (int i = 0; i < len; i++)
{
lookaheads[i] = data.readShort();
}
len = data.readInt();
actn_offsets = new int[len];
for (int i = 0; i < len; i++)
{
actn_offsets[i] = data.readInt();
}
goto_offsets = new int[len];
for (int i = 0; i < len; i++)
{
goto_offsets[i] = data.readInt();
}
len = data.readInt();
compressed = len != 0;
if (compressed)
{
default_actions = new short[len];
for (int i = 0; i < len; i++)
{
default_actions[i] = data.readShort();
}
}
else
{
default_actions = null;
}
int min_nt_id = Integer.MAX_VALUE;
len = data.readInt();
rule_infos = new int[len];
for (int i = 0; i < len; i++)
{
rule_infos[i] = data.readInt();
min_nt_id = Math.min(min_nt_id, rule_infos[i] >>> 16);
}
n_term = min_nt_id;
error_symbol_id = data.readShort();
}
finally
{
data.close();
}
}
catch (IOException e)
{
throw new IllegalStateException("cannot initialize parser tables: " + e.getMessage());
}
}
/**
* Scans lookaheads expected in a given state for a terminal symbol.
* Used in error recovery when an unexpected terminal is replaced with one that is expected.
*
* @param state in which error occured
* @return ID of the expected terminal symbol or -1 if there is none
*/
final short findFirstTerminal(int state)
{
int offset = actn_offsets[state];
for (short term_id = offset < 0 ? (short) -offset : 0; term_id < n_term; term_id++)
{
int index = offset + term_id;
if (index >= lookaheads.length)
break;
if (lookaheads[index] == term_id)
return term_id;
}
return -1;
}
/**
* Find the appropriate action for a parser in a given state with a specified terminal look-ahead.
*
* @param state of a parser
* @param lookahead
* @return parser action
*/
final short findParserAction(int state, short lookahead)
{
int index = actn_offsets[state];
if (index != UNUSED_OFFSET)
{
index += lookahead;
if (0 <= index && index < actions.length && lookaheads[index] == lookahead)
{
return actions[index];
}
}
return compressed ? default_actions[state] : 0;
}
/**
* Find the appropriate action for a parser in a given state with a specified nonterminal look-ahead.
* In this case the only possible outcomes are either a state to shift to or an accept action.
*
* @param state of a parser
* @param lookahead
* @return parser action
*/
final short findNextState(int state, short lookahead)
{
int index = goto_offsets[state];
if (index != UNUSED_OFFSET)
{
index += lookahead;
if (0 <= index && index < actions.length && lookaheads[index] == lookahead)
{
return actions[index];
}
}
return compressed ? default_actions[state] : 0;
}
static final int UNUSED_OFFSET = Integer.MIN_VALUE;
static byte[] decode(String spec)
{
char[] chars = spec.toCharArray();
if (chars.length % 4 != 0)
throw new IllegalArgumentException("corrupted encoding");
int len = chars.length / 4 * 3;
byte[] bytes = new byte[chars[chars.length - 1] == '=' ? chars[chars.length - 2] == '=' ? len - 2 : len - 1 : len];
len -= 3;
int ci = 0, bi = 0;
while (bi < len)
{
int acc = decode(chars[ci++]) << 18 | decode(chars[ci++]) << 12 | decode(chars[ci++]) << 6 | decode(chars[ci++]);
bytes[bi++] = (byte) (acc >> 16);
bytes[bi++] = (byte) (acc >> 8 & 0xFF);
bytes[bi++] = (byte) (acc & 0xFF);
}
int acc = decode(chars[ci++]) << 18 | decode(chars[ci++]) << 12 | decode(chars[ci++]) << 6 | decode(chars[ci++]);
bytes[bi++] = (byte) (acc >> 16);
if (bi < bytes.length)
{
bytes[bi++] = (byte) (acc >> 8 & 0xFF);
if (bi < bytes.length)
{
bytes[bi++] = (byte) (acc & 0xFF);
}
}
return bytes;
}
static int decode(char c)
{
if (c <= '9')
{
if (c >= '0')
return c - '0';
if (c == '#')
return 62;
if (c == '$')
return 63;
}
else if (c <= 'Z')
{
if (c >= 'A')
return c - 'A' + 10;
if (c == '=')
return 0;
}
else if ('a' <= c && c <= 'z')
return c - 'a' + 36;
throw new IllegalStateException("illegal encoding character '" + c + "'");
}
static InputStream getSpecAsResourceStream(Class impl_class)
{
String name = impl_class.getName();
name = name.substring(name.lastIndexOf('.') + 1) + ".spec";
InputStream spec_stream = impl_class.getResourceAsStream(name);
if (spec_stream == null)
throw new IllegalStateException("parser specification not found");
return spec_stream;
}
}