Mercurial > hg > truffle
diff graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java @ 17066:0bcefb0f8488
Truffle: byte[] sources.
author | Chris Seaton <chris.seaton@oracle.com> |
---|---|
date | Mon, 08 Sep 2014 22:21:21 +0100 |
parents | 19faa7ca37c1 |
children | c88ab4f1f04a |
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Mon Sep 08 13:49:40 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Mon Sep 08 22:21:21 2014 +0100 @@ -158,6 +158,37 @@ } /** + * Creates a source from raw bytes. This can be used if the encoding of strings in your language + * is not compatible with Java strings, or if your parser returns byte indices instead of + * character indices. The returned source is then indexed by byte, not by character. + * + * @param bytes the raw bytes of the source + * @param description a note about the origin, possibly useful for debugging + * @param decoder how to decode the bytes into Java strings + * @return a newly created, non-indexed source representation + */ + public static Source fromBytes(byte[] bytes, String description, BytesDecoder decoder) { + return fromBytes(bytes, 0, bytes.length, description, decoder); + } + + /** + * Creates a source from raw bytes. This can be used if the encoding of strings in your language + * is not compatible with Java strings, or if your parser returns byte indices instead of + * character indices. The returned source is then indexed by byte, not by character. Offsets are + * relative to byteIndex. + * + * @param bytes the raw bytes of the source + * @param byteIndex where the string starts in the byte array + * @param length the length of the string in the byte array + * @param description a note about the origin, possibly useful for debugging + * @param decoder how to decode the bytes into Java strings + * @return a newly created, non-indexed source representation + */ + public static Source fromBytes(byte[] bytes, int byteIndex, int length, String description, BytesDecoder decoder) { + return new BytesSource(description, bytes, byteIndex, length, decoder); + } + + /** * Creates a source from literal text, but which acts as a file and can be retrieved by name * (unlike other literal sources); intended for testing. * @@ -246,6 +277,10 @@ */ public abstract String getCode(); + public String getCode(int charIndex, int charLength) { + return getCode().substring(charIndex, charIndex + charLength); + } + /** * Gets the text (not including a possible terminating newline) in a (1-based) numbered line. */ @@ -368,10 +403,7 @@ * @throws IllegalStateException if the source is one of the "null" instances */ public final SourceSection createSection(String identifier, int charIndex, int length) throws IllegalArgumentException { - final int codeLength = getCode().length(); - if (!(charIndex >= 0 && length >= 0 && charIndex + length <= codeLength)) { - throw new IllegalArgumentException("text positions out of range"); - } + checkRange(charIndex, length); checkTextMap(); final int startLine = getLineNumber(charIndex); final int startColumn = charIndex - getLineStartOffset(startLine) + 1; @@ -379,6 +411,12 @@ return new DefaultSourceSection(this, identifier, startLine, startColumn, charIndex, length); } + protected void checkRange(int charIndex, int length) { + if (!(charIndex >= 0 && length >= 0 && charIndex + length <= getCode().length())) { + throw new IllegalArgumentException("text positions out of range"); + } + } + /** * Creates a representation of a line of text in the source identified only by line number, from * which the character information will be computed. @@ -409,15 +447,19 @@ private TextMap checkTextMap() { if (textMap == null) { - final String code = getCode(); - if (code == null) { - throw new RuntimeException("can't read file " + getName()); - } - textMap = new TextMap(code); + textMap = createTextMap(); } return textMap; } + protected TextMap createTextMap() { + final String code = getCode(); + if (code == null) { + throw new RuntimeException("can't read file " + getName()); + } + return TextMap.fromString(code); + } + private static final class LiteralSource extends Source { private final String name; // Name used originally to describe the source @@ -621,6 +663,74 @@ } + private static final class BytesSource extends Source { + + private final String name; + private final byte[] bytes; + private final int byteIndex; + private final int length; + private final BytesDecoder decoder; + + public BytesSource(String name, byte[] bytes, int byteIndex, int length, BytesDecoder decoder) { + this.name = name; + this.bytes = bytes; + this.byteIndex = byteIndex; + this.length = length; + this.decoder = decoder; + } + + @Override + protected void reset() { + } + + @Override + public String getName() { + return name; + } + + @Override + public String getShortName() { + return name; + } + + @Override + public String getPath() { + return name; + } + + @Override + public URL getURL() { + return null; + } + + @Override + public Reader getReader() { + return null; + } + + @Override + public String getCode() { + return decoder.decode(bytes, byteIndex, length); + } + + @Override + public String getCode(int byteOffset, int codeLength) { + return decoder.decode(bytes, byteIndex + byteOffset, codeLength); + } + + @Override + protected void checkRange(int charIndex, int rangeLength) { + if (!(charIndex >= 0 && rangeLength >= 0 && charIndex + rangeLength <= length)) { + throw new IllegalArgumentException("text positions out of range"); + } + } + + @Override + protected TextMap createTextMap() { + return TextMap.fromBytes(bytes, byteIndex, length, decoder); + } + } + private static final class DefaultSourceSection implements SourceSection { private final Source source; @@ -704,7 +814,7 @@ @Override public final String getCode() { - return getSource().getCode().substring(charIndex, charIndex + charLength); + return getSource().getCode(charIndex, charLength); } @Override @@ -866,12 +976,18 @@ // Is the final text character a newline? final boolean finalNL; + public TextMap(int[] nlOffsets, int textLength, boolean finalNL) { + this.nlOffsets = nlOffsets; + this.textLength = textLength; + this.finalNL = finalNL; + } + /** * Constructs map permitting translation between 0-based character offsets and 1-based * lines/columns. */ - public TextMap(String text) { - this.textLength = text.length(); + public static TextMap fromString(String text) { + final int textLength = text.length(); final ArrayList<Integer> lines = new ArrayList<>(); lines.add(0); int offset = 0; @@ -887,12 +1003,37 @@ } lines.add(Integer.MAX_VALUE); - nlOffsets = new int[lines.size()]; + final int[] nlOffsets = new int[lines.size()]; for (int line = 0; line < lines.size(); line++) { nlOffsets[line] = lines.get(line); } - finalNL = textLength > 0 && (textLength == nlOffsets[nlOffsets.length - 2]); + final boolean finalNL = textLength > 0 && (textLength == nlOffsets[nlOffsets.length - 2]); + + return new TextMap(nlOffsets, textLength, finalNL); + } + + public static TextMap fromBytes(byte[] bytes, int byteIndex, int length, BytesDecoder bytesDecoder) { + final ArrayList<Integer> lines = new ArrayList<>(); + lines.add(0); + + bytesDecoder.decodeLines(bytes, byteIndex, length, new BytesDecoder.LineMarker() { + + public void markLine(int index) { + lines.add(index); + } + }); + + lines.add(Integer.MAX_VALUE); + + final int[] nlOffsets = new int[lines.size()]; + for (int line = 0; line < lines.size(); line++) { + nlOffsets[line] = lines.get(line); + } + + final boolean finalNL = length > 0 && (length == nlOffsets[nlOffsets.length - 2]); + + return new TextMap(nlOffsets, length, finalNL); } /**