/**
* Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
*
* 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 de.codesourcery.jasm16.ast;
import java.util.ArrayList;
import java.util.List;
import de.codesourcery.jasm16.compiler.ICompilationContext;
import de.codesourcery.jasm16.compiler.ISymbolTable;
import de.codesourcery.jasm16.exceptions.ParseException;
import de.codesourcery.jasm16.lexer.TokenType;
import de.codesourcery.jasm16.parser.IParseContext;
import de.codesourcery.jasm16.utils.TextRegion;
/**
* An AST node that represents a character literal (characters enclosed by
* {@link TokenType#STRING_DELIMITER}.
*
* @author tobias.gierke@code-sourcery.de
*/
public class CharacterLiteralNode extends ConstantValueNode {
public static final int UNLIMITED_LENGTH = -1;
private final int maxLength;
private String value;
private byte[] bytes;
public CharacterLiteralNode() {
maxLength = UNLIMITED_LENGTH;
}
public CharacterLiteralNode(int maxLength)
{
if ( maxLength <= 0 && maxLength != UNLIMITED_LENGTH ) {
throw new IllegalArgumentException("Invalid max. length: "+maxLength);
}
this.maxLength = maxLength;
}
@Override
protected ASTNode parseInternal(IParseContext context) throws ParseException
{
if ( ! context.peek().hasType( TokenType.STRING_DELIMITER ) ) {
throw new ParseException("Expected a character literal but string delimiter is missing",context.currentParseIndex(),1);
}
TextRegion region = new TextRegion( context.currentParseIndex() , 0 );
value = context.parseString( region );
mergeWithAllTokensTextRegion( region );
if ( maxLength != UNLIMITED_LENGTH && value.length() > maxLength ) {
context.addCompilationError( "String literal too long, expected at most "+maxLength+" characters",this);
}
this.bytes = toByteArray( getBytes() );
return this;
}
private byte[] toByteArray(List<Integer> data) {
final byte[] result = new byte[data.size()];
int index = 0;
for ( Integer value : data ) {
result[index++] = (byte) ( value & 0xff);
}
return result;
}
public List<Integer> getBytes() throws ParseException
{
List<Integer> result = new ArrayList<Integer>();
int offset = 0;
for ( char c : value.toCharArray() ) {
int value = c;
if ( value < 0 ) {
value += 256;
}
if ( value < 0 || value > 255 )
{
throw new ParseException("Invalid character '"+c+"' at index "+offset,offset,1);
}
/*
* Characters in DCPU are assumed to always require 16-bit with
* the first byte being used for foreground/background color and
* and second byte containing the ASCII code.
*
* TODO: Check character literal conversion .... actual character conversion being used is unconfirmed yet...
*
* http://stackoverflow.com/questions/10038816/is-dcpu-16-assembler-dat-with-a-string-supposed-to-generate-a-byte-or-word-per
*/
result.add( 0 ); // no color flags
result.add( value );
offset++;
}
return result;
}
@Override
protected CharacterLiteralNode copySingleNode()
{
final CharacterLiteralNode result=new CharacterLiteralNode(this.maxLength);
result.value = value;
if ( this.bytes != null ) {
result.bytes = new byte[ this.bytes.length ];
System.arraycopy( this.bytes , 0 , result.bytes , 0 , this.bytes.length );
}
return result;
}
@Override
public boolean supportsChildNodes() {
return false;
}
@Override
public Long getNumericValue(ISymbolTable symbolTable)
{
return calculate( symbolTable );
}
@Override
public Long calculate(ISymbolTable symbolTable) {
if ( this.bytes == null || this.bytes.length > 2 ) {
return null;
}
final int hi = bytes[0] & 0xff;
final int lo = bytes[1] & 0xff;
return (long) (hi << 8 ) | lo;
}
@Override
public TermNode reduce(ICompilationContext context) {
return (TermNode) createCopy( true );
}
}