/* --------------------------------------------------------- * * __________ 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; } }