/* --------------------------------------------------------- *
* __________ D E L T A S C R I P T *
* (_________() *
* / === / - A fast, dynamic scripting language *
* | == | - Version 4.13.11.0 *
* / === / - Developed by Adam R. Nelson *
* | = = | - 2011-2013 *
* / === / - Distributed under GNU LGPL v3 *
* (________() - http://github.com/ar-nelson/deltascript *
* *
* --------------------------------------------------------- */
package com.sector91.delta.script.instrs;
import java.util.Arrays;
import com.sector91.delta.script.DScriptErr;
import com.sector91.delta.script.DScriptContext;
import com.sector91.delta.script.DScriptExecutor;
import com.sector91.delta.script.annotations.DSDynamicField;
import com.sector91.delta.script.annotations.DSName;
import com.sector91.delta.script.annotations.DSType;
import com.sector91.delta.script.objects.DS_AbstractObject;
import com.sector91.delta.script.objects.DS_Object;
import com.sector91.delta.script.objects.DS_Scope;
import com.sector91.delta.script.objects.DS_Tag;
import com.sector91.delta.script.objects.reflect.DS_JavaClass;
/**
* <p>A compiled DeltaScript instruction, which is similar to a Lisp form.</p>
*
* <p>DeltaScript instructions are superficially similar to Lisp forms, but not
* identical. While Lisp forms are linked lists of S-expressions, DeltaScript
* instructions have an {@code int} <i>type</i>, a {@code String} <i>head</i>,
* and a <i>tail</i> consisting of zero or more sub-instructions. They are
* typically rendered as {@code (type:head tail0 tail1 ... tailn)}, with the
* {@code :head} omitted if <i>head</i> is {@code null} and the parentheses
* omitted if there are no sub-instructions.</p>
*
* <p>DSInstrs are immutable objects, and there should be no conceivable need
* to modify one.</p>
*
* @author Adam R. Nelson
* @version 4.13.11.0
*/
@DSType("Instr")
public abstract class DSInstr extends DS_AbstractObject
{
public static final String TYPE_NAME = "Instr";
private static final DS_JavaClass DSCLASS = DS_JavaClass.fromClass(
DSInstr.class);
private final int start;
private final short len;
protected final String head;
protected final DSInstr[] tail;
public static DSInstr create(InstrType type, DScriptContext context,
String head, DSInstr... tail)
{return create(type, context, -1, 0, head, tail);}
public static DSInstr create(InstrType type, DScriptContext context,
int start, int len, String head, DSInstr... tail)
{
final short slen = (short)(Math.min(len, Short.MAX_VALUE));
switch (type)
{
case NONE: return new NoneInstr(start, slen, head, tail);
case SCALAR: return new ScalarInstr(start, slen, context, head, tail);
case TAG: return new TagInstr(start, slen, head, tail);
case STRING: return new StringInstr(start, slen, head, tail);
case ARRAY: return new ArrayInstr(start, slen, head, tail);
case MAP: return new MapInstr(start, slen, head, tail);
case VECTOR: return new VectorInstr(start, slen, head, tail);
case SCOPE: return new ScopeInstr(start, slen, head, tail);
case FUNC: return new FuncInstr(start, slen, head, tail);
case GET: return new GetInstr(start, slen, head, tail);
case SET: return new SetInstr(start, slen, head, tail);
case DOTGET: return new DotGetInstr(start, slen, head, tail);
case DOTSET: return new DotSetInstr(start, slen, head, tail);
case DEF: return new DefInstr(start, slen, head, tail);
case $VAR: return new SpecialVarInstr(start, slen, head, tail);
case DOT$VAR: return new DotSpecialVarInstr(start, slen, head, tail);
case INDEX: return new IndexInstr(start, slen, head, tail);
case INDEXSET:return new IndexSetInstr(start, slen, head, tail);
case FIELD: return new FieldInstr(start, slen, head, tail);
case DO: return new DoInstr(start, slen, head, tail);
case IF: return new IfInstr(start, slen, head, tail);
case COND: return new CondInstr(start, slen, head, tail);
case BRANCH: return new BranchInstr(start, slen, head, tail);
case LOOP: return new LoopInstr(start, slen, head, tail);
case FOR: return new ForInstr(start, slen, head, tail);
case RETURN: return new ReturnInstr(start, slen, head, tail);
case BREAK: return new BreakInstr(start, slen, head, tail);
case CONTINUE:return new ContinueInstr(start, slen, head, tail);
case EXPAND: return new ExpandInstr(start, slen, head, tail);
case COMPRH: return new ComprehensionInstr(start, slen, head, tail);
case OP: return new OpInstr(start, slen, head, tail);
case TYPECHK: return new TypeChkInstr(start, slen, head, tail);
case EQ: return new EqInstr(start, slen, head, tail);
case NE: return new NeInstr(start, slen, head, tail);
case LT: return new LtInstr(start, slen, head, tail);
case LE: return new LeInstr(start, slen, head, tail);
case GT: return new GtInstr(start, slen, head, tail);
case GE: return new GeInstr(start, slen, head, tail);
case NOT: return new NotInstr(start, slen, head, tail);
case AND: return new AndInstr(start, slen, head, tail);
case OR: return new OrInstr(start, slen, head, tail);
case CALL: return new CallInstr(start, slen, head, tail);
case STDLIB: return new StdLibInstr(start, slen, head, tail);
case INCLUDE: return new IncludeInstr(start, slen, head, tail);
default: throw new IllegalArgumentException("Unrecognized instr type: "+
type);
}
}
protected DSInstr(int start, short len, String head, DSInstr... tail)
{
this.start = start;
this.len = len;
this.head = head;
this.tail = tail;
}
public abstract DS_Object exec(DS_Scope scope, DScriptExecutor executor)
throws DScriptErr;
/** Returns this instruction's type, which determines how it is executed.
* @see #name() */
public abstract InstrType type();
/** Returns the human-readable name of this instruction's {@link #type()}.*/
@DSName("typeStr") @DSDynamicField
public final String name()
{return type().name;}
@DSName("type") @DSDynamicField
public final DS_Tag nameTag()
{return type().tag;}
/** Returns the index of the first character of the block of DeltaScript
* code that this instruction was compiled from, or -1 if this instruction
* was not compiled with debug information. */
public final int sourceStart()
{return start;}
/** Returns the (possibly truncated) length of the block of DeltaScript
* code that this instruction was compiled from, or 0 if this instruction
* was not compiled with debug information. */
public final short sourceLength()
{return len;}
/** Returns this instruction's <i>head</i> (data string), which may be null.
* @see #isHeadless() */
@DSName("head") @DSDynamicField
public String head()
{return head;}
/** Returns this instruction's <i>tail</i> (subinstrs).
* @see #isSingleton() */
@DSName("tail") @DSDynamicField
public DSInstr[] tail()
{return tail;}
/** Returns {@code false} if this instruction has a <i>head</i> (data
* string).
* @see #head() */
@DSName("headless") @DSDynamicField
public boolean isHeadless()
{return head == null;}
/** Returns {@code false} if this instruction has a <i>tail</i> (subinstrs).
* @see #tail() */
@DSName("singleton") @DSDynamicField
public boolean isSingleton()
{return tail.length == 0;}
@Override public String toString()
{
String name = name();
if (isSingleton())
return name + (isHeadless()?"":":"+head);
else
{
String output = "(" + name + (isHeadless()?"":":"+head);
for (int i=0;i<tail.length;i++)
output += " " + (tail[i] == null ? "NULL" : tail[i].toString());
return output + ")";
}
}
public String prettyPrint()
{return prettyPrint(0, false);}
private String prettyPrint(int indent, boolean isIndented)
{
StringBuffer buffer = new StringBuffer();
if (isIndented)
for (int i=0; i<indent; i++)
buffer.append(' ');
// Test if this instr should be split vertically.
boolean splitVert = false;
for (DSInstr i : tail)
if (i != null && !i.isSingleton())
splitVert = true;
if (!splitVert)
buffer.append(toString());
else
{
String start = "(" + name() + (isHeadless()?"":":"+head) +" ";
int nextIndent = indent + start.length();
buffer.append(start);
try
{buffer.append(tail[0].prettyPrint(nextIndent, false));}
catch (NullPointerException ex)
{buffer.append("NULL POINTER EXCEPTION");}
for (int i=1; i<tail.length; i++)
{
try
{buffer.append("\n"+tail[i].prettyPrint(nextIndent, true));}
catch (NullPointerException ex)
{
buffer.append('\n');
for (int j=0; j<nextIndent; j++)
buffer.append(' ');
buffer.append("NULL POINTER EXCEPTION");
}
}
buffer.append(")");
}
// Return the finished buffer.
return buffer.toString();
}
/**
* <p>Returns a binary representation of this DSInstr. Used internally by
* {@link com.sector91.delta.script.CompiledDeltaScript#toBinary()}.</p>
*/
public String toBinary()
{
StringBuffer buffer = new StringBuffer();
buffer.append((char)(200+type().opcode));
buffer.append(head==null?(char)190:head);
for (int i=0; i<tail.length; i++)
buffer.append(tail[i].toBinary());
buffer.append((char)191);
return buffer.toString();
}
protected DScriptErr rethrow(RuntimeException ex)
{
return new DScriptErr(ex.getMessage(), ex, this);
}
protected DScriptErr rethrow(DScriptErr ex)
{
if (ex.getSourceInstr() == null)
return new DScriptErr(ex.getMessage(), ex, this);
else
return ex;
}
/** Accepts and processes a visitor object that will visit (up to) all of
* this instruction's subinstrs. */
public InstrVisitorResult accept(InstrVisitor visitor)
{
final InstrVisitorResult result = visitor.visit(this);
if (result == InstrVisitorResult.SKIP_TAIL ||
result == InstrVisitorResult.STOP)
return result;
for (DSInstr instr : tail())
if (instr.accept(visitor) == InstrVisitorResult.STOP)
return InstrVisitorResult.STOP;
return result;
}
@Override protected DS_JavaClass getDeltaScriptClass()
{return DSCLASS;}
public String getTypeName()
{return TYPE_NAME;}
public DSInstr unbox()
{return this;}
public boolean equals(DS_Object other)
{
if (other instanceof DSInstr)
{
final DSInstr i = (DSInstr)other;
return i.type() == type() &&
((i.head() == null && head() == null) ||
i.head().equals(head())) &&
Arrays.equals(i.tail(), tail());
}
return false;
}
@Override public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + type().opcode;
result = prime * result + ((head == null) ? 0 : head.hashCode());
result = prime * result + Arrays.hashCode(tail);
return result;
}
}