[chuck-dev] instanceof operator

Graham Coleman gc at gehennom.net
Sat Mar 25 18:06:27 EST 2006


Subject: instanceof operator

Fellow ChucKers,

I was looking for a way to wrap functionality over different instrument
classes. My strategy for this is to determine the object's class at
runtime, and cast it to an instance of that class. To support this I added
a new operator, that works somewhat like 'instanceof' in the Java
language.

It's not a common use operator, but may be useful for those who want to
build larger OO programs in ChucK.

I have attached three files:
instanceof5.patch - patch from 'diff -ur' on my version vs. v2 sources (* 1 2)
instanceof.ck - basic demonstation of operator functionality
StkWrap.ck - proof of concept ugen wrapper - wraps ugens, plays notes

If this is useful, feel free to modify this for language design concerns.
Or, I can take suggestions for modifications.

Here's what I did:

1. Added an INSTANCEOF token to chuck.lex.
2. Added an instanceof_expression to chuck.y.
3. In chuck_emit.cpp, it emits a new VM instruction. It binds the
   right-hand type at compile time.
4. The implementation of the instruction:
  a. pops two words off the register stack (disregarding the rhs)
  b. compares the actual_type of the static right-hand type
  c. with the left-hand type
  d. if it doesn't match, try the parent of the left-hand type
  e. until parent is NULL
  f. pushes a 1 or 0 to the regular stack

* I tried to make the patch using a cvs diff, but something breaks in my
  client TortoiseCVS when I try to make the patch vs. anon-chuck. So, I
  used 'diff -ur' against a clean copy of v2 and filtered out the
  generated files.

In C:\Documents and Settings\Graham\My Documents\projects\chuck_dev:
"C:\Program Files\TortoiseCVS\cvs.exe"
"-q" "diff" "-u" "v2" CVSROOT=:ext:anon-chuck at cvs.cs.princeton.edu:/cvs

cvs.exe diff: warning: unrecognized response `Could not chdir to home
directory /cvs/projects/chuck/homedirs/anon-chuck: Permission denied' from
cvs server

1. chuck_win32.c is omitted from this patch. You can generate it in the
usual way from the flex and bison files. If it would save time, I can send
it.

2. Could someone test this on mac or unix?

much respect to the chuck team,

Graham
-------------- next part --------------
// basic tests of instanceof operator
// setup for replicants

// patch
Rhodey voc=> JCRev r => Echo a => Echo b => Echo c => dac;

220.0 => voc.freq;
0.8 => voc.gain;
.8 => r.gain;
.2 => r.mix;
1000::ms => a.max => b.max => c.max;
750::ms => a.delay => b.delay => c.delay;
.50 => a.mix => b.mix => c.mix;

//test out the new operator
<<<(a instanceof TubeBell)>>> => int fa;
<<<(voc instanceof Rhodey)>>> => int tr;
<<<(dac instanceof TubeBell)>>> => fa;
<<<(r instanceof JCRev)>>> => tr;
<<<(c instanceof Echo)>>> => tr;
<<<(c instanceof UGen)>>> => tr;
<<<(voc instanceof FM)>>> => tr;

-------------- next part --------------
//this demonstrates using instanceof and casting

//to wrap functionality over different classes

//Graham Coleman, gc at gehennom.net

public class StkWrap {



	UGen @ inst;

	

	fun void set( UGen u ) {

		u @=> inst;

	}



	public void freq( float f ) {



		if ( inst instanceof Rhodey ) {

			(inst $ Rhodey) @=> Rhodey @ r;

			r.freq( f );

		}



		else if ( inst instanceof ModalBar ) {

			(inst $ ModalBar).freq( f );

        }

	}



	public void noteOn( float f ) {

		

		if ( inst instanceof Rhodey ) {

			(inst $ Rhodey) @=> Rhodey @ r;

			r.noteOn( f );

		}



		else if ( inst instanceof ModalBar ) {

			(inst $ ModalBar).noteOn( f );

        }



    }

}



//two unit generators

Rhodey r => dac;

ModalBar m => dac;

m.gain( 3 );



//two wrappers

StkWrap wrap;

wrap.set( r );

StkWrap wrap2;

wrap2.set( m );



//play notes

wrap.freq( 440 );

wrap.noteOn( 0.5 );



1::second => now;



wrap2.freq( 880 );

wrap2.noteOn( 0.8 );



5::second => now;
-------------- next part --------------
diff -ur --exclude=CVS --exclude=chuck_win32* ..\..\chuck_clean\chuck_dev\v2\chuck.lex .\chuck.lex
--- ..\..\chuck_clean\chuck_dev\v2\chuck.lex	Sun Dec 04 19:40:25 2005
+++ .\chuck.lex	Sat Mar 11 18:57:52 2006
@@ -259,6 +259,7 @@
 "%=>"                   { adjust(); return PERCENT_CHUCK; }
 "@"                     { adjust(); return AT_SYM; }
 "@@"                    { adjust(); return ATAT_SYM; }
+instanceof              { adjust(); return INSTANCEOF; }
 
 0[xX][0-9a-fA-F]+{IS}?  { adjust(); yylval.ival=htol(yytext); return NUM; }
 0[cC][0-7]+{IS}?        { adjust(); yylval.ival=atoi(yytext); return NUM; }
diff -ur --exclude=CVS --exclude=chuck_win32* ..\..\chuck_clean\chuck_dev\v2\chuck.y .\chuck.y
--- ..\..\chuck_clean\chuck_dev\v2\chuck.y	Sun Feb 26 19:00:47 2006
+++ .\chuck.y	Tue Mar 21 14:28:58 2006
@@ -107,6 +107,7 @@
   UNCHUCK CLASS INTERFACE EXTENDS IMPLEMENTS
   PUBLIC PROTECTED PRIVATE STATIC ABSTRACT CONST 
   SPORK L_HACK R_HACK
+  INSTANCEOF
 
 
 %type <program> program
@@ -136,6 +137,9 @@
 %type <exp> exclusive_or_expression
 %type <exp> and_expression
 %type <exp> equality_expression
+
+%type <exp> instanceof_expression
+
 %type <exp> relational_expression
 %type <exp> shift_expression
 %type <exp> additive_expression
@@ -432,6 +436,7 @@
         
 and_expression
         : equality_expression               { $$ = $1; }
+| instanceof_expression { $$ = $1; }
         | and_expression S_AND equality_expression
             { $$ = new_exp_from_binary( $1, ae_op_s_and, $3, EM_lineNum ); }
         ;
@@ -443,6 +448,11 @@
         | equality_expression NEQ relational_expression
             { $$ = new_exp_from_binary( $1, ae_op_neq, $3, EM_lineNum ); }
         ;
+
+instanceof_expression
+: equality_expression INSTANCEOF equality_expression 
+{ $$ = new_exp_from_binary( $1, ae_op_instanceof, $3, EM_lineNum ); }
+;
 
 relational_expression
         : shift_expression                  { $$ = $1; }
diff -ur --exclude=CVS --exclude=chuck_win32* ..\..\chuck_clean\chuck_dev\v2\chuck_absyn.cpp .\chuck_absyn.cpp
--- ..\..\chuck_clean\chuck_dev\v2\chuck_absyn.cpp	Sun Feb 26 19:00:47 2006
+++ .\chuck_absyn.cpp	Thu Mar 23 23:21:51 2006
@@ -858,7 +858,8 @@
   "spork ~",
   "typeof",
   "sizeof",
-  "new"
+  "new",
+  "instanceof"
 };
 
 
diff -ur --exclude=CVS --exclude=chuck_win32* ..\..\chuck_clean\chuck_dev\v2\chuck_absyn.h .\chuck_absyn.h
--- ..\..\chuck_clean\chuck_dev\v2\chuck_absyn.h	Sun Feb 26 19:00:47 2006
+++ .\chuck_absyn.h	Thu Mar 23 23:24:39 2006
@@ -54,7 +54,7 @@
     ae_op_shift_left_chuck, ae_op_percent_chuck, ae_op_s_chuck,
     ae_op_plusplus, ae_op_minusminus, ae_op_tilda, ae_op_exclamation,
     ae_op_at_chuck, ae_op_unchuck, ae_op_spork, ae_op_typeof,
-    ae_op_sizeof, ae_op_new
+    ae_op_sizeof, ae_op_new, ae_op_instanceof
 } ae_Operator;
 
 const char * op2str( ae_Operator op );
diff -ur --exclude=CVS --exclude=chuck_win32* ..\..\chuck_clean\chuck_dev\v2\chuck_emit.cpp .\chuck_emit.cpp
--- ..\..\chuck_clean\chuck_dev\v2\chuck_emit.cpp	Sun Feb 26 19:33:43 2006
+++ .\chuck_emit.cpp	Thu Mar 23 23:23:10 2006
@@ -1820,6 +1820,12 @@
         }
         break;
 
+	//the new instanceof operator
+	case ae_op_instanceof: 
+		//static type reference for right-hand class
+		emit->append( instr = new Chuck_Instr_Instanceof( t_right ) );
+		break;
+
     default:
         EM_error2( lhs->linepos,
             "(emit): internal error: unhandled op '%s' %s '%s'",
@@ -3586,11 +3592,14 @@
     }
     else
     {
+		//classes should end up here
+
         // special case
         if( v->func_ref )
             emit->append( new Chuck_Instr_Reg_Push_Imm( (t_CKUINT)v->func_ref ) );
         // check size
         else if( v->type->size == 4 )
+			//this is getting called for refs and Class refs
             emit->append( new Chuck_Instr_Reg_Push_Mem( v->offset, v->is_context_global ) );
         else if( v->type->size == 8 )
             emit->append( new Chuck_Instr_Reg_Push_Mem2( v->offset, v->is_context_global  ) );
diff -ur --exclude=CVS --exclude=chuck_win32* ..\..\chuck_clean\chuck_dev\v2\chuck_instr.cpp .\chuck_instr.cpp
--- ..\..\chuck_clean\chuck_dev\v2\chuck_instr.cpp	Mon Feb 27 01:07:22 2006
+++ .\chuck_instr.cpp	Fri Mar 24 12:43:26 2006
@@ -1343,8 +1343,50 @@
     push_( sp, val_(sp) == val_(sp+1) );
 }
 
+//-----------------------------------------------------------------------------
+// name: execute()
+// desc: compares the static rhs class with the lhs type on the stack
+//-----------------------------------------------------------------------------
+void Chuck_Instr_Instanceof::execute( Chuck_VM * vm, Chuck_VM_Shred * shred )
+{
+	t_CKINT *& sp = (t_CKINT *&)shred->reg->sp;
+    t_CKUINT *& reg_sp = (t_CKUINT *&)shred->reg->sp;
 
+	//we need to pop lhs and rhs off the reg_stack
+    pop_( reg_sp, 2 ); //two words
 
+    //get the symbol reference on the right hand side
+    Chuck_Object *lhs = (Chuck_Object *)*reg_sp;
+	//get the symbol reference for the class, but it seems stale
+	Chuck_Object *rhs = (Chuck_Object *)*(reg_sp + 1);
+    
+	// if lhs is null, just return false
+	if( !lhs ) {
+		push_( sp, 0 );
+		return;
+	}
+
+	//compare the actual type of the class to the runtime type of the ref
+	//to make this legitimate, I'll have to traverse the tree
+
+	Chuck_Type *curr = lhs->type_ref;
+
+	while( curr != NULL ) {
+		//if either the class or some superclass of lhs matches rhs class
+		if ( curr == this->class_type->actual_type ) {
+			push_( sp, 1 );
+			return;
+		}
+		else {
+			curr = curr->parent;
+		}
+	}
+
+	//if we passed the loop, the answer is no
+	push_( sp, 0 );
+	return;
+
+}
 
 //-----------------------------------------------------------------------------
 // name: execute()
diff -ur --exclude=CVS --exclude=chuck_win32* ..\..\chuck_clean\chuck_dev\v2\chuck_instr.h .\chuck_instr.h
--- ..\..\chuck_clean\chuck_dev\v2\chuck_instr.h	Tue Oct 04 17:30:55 2005
+++ .\chuck_instr.h	Thu Mar 23 23:25:22 2006
@@ -765,7 +765,21 @@
 };
 
 
+//-----------------------------------------------------------------------------
+// name: struct Chuck_Instr_Eq_int
+// desc: a static type stores the compile-time rhs class
+//-----------------------------------------------------------------------------
+{
+public:
+	Chuck_Instr_Instanceof( Chuck_Type *t )
+	{ this->class_type = t; }
 
+    virtual void execute( Chuck_VM * vm, Chuck_VM_Shred * shred );
+
+public:
+	Chuck_Type * class_type;
+};
 
 //-----------------------------------------------------------------------------
 // name: struct Chuck_Instr_Neq_int
diff -ur --exclude=CVS --exclude=chuck_win32* ..\..\chuck_clean\chuck_dev\v2\chuck_type.cpp .\chuck_type.cpp
--- ..\..\chuck_clean\chuck_dev\v2\chuck_type.cpp	Sun Feb 26 19:04:28 2006
+++ .\chuck_type.cpp	Thu Mar 23 23:24:17 2006
@@ -1386,7 +1386,13 @@
         LR( te_time, te_time ) return &t_int;
         if( isa( left, &t_object ) && isa( right, &t_object ) ) return &t_int;
     break;
+
+	//(object X class) returns an int
+	case ae_op_instanceof:
+		if ( isa( left, &t_object ) && 
+			 isa( right, &t_class ) ) return &t_int;
+		break;
     
     case ae_op_s_and_chuck:
     case ae_op_s_or_chuck:


More information about the chuck-dev mailing list