Example usage:
% load ./rpn.so % rpnwide 1 2 + 3 * 9 % rpnfloat 1.0 3.0 * 9.0 - -6.0 % rpn 3 3 / 1
/* * RPN C Extension * * Copyright 2006, 2008 George Peter Staplin * * Version 5 - Sep 1, 2008 (updated to fix a memory leak and use Tcl_ObjPrintf) * Version 4 - Aug 21, 2006 (released to the world) * Version 3 - July 25, 2006 (specialization and better algorithms) * Version 2 - July 25, 2006 (much faster from custom Tcl_ObjType types) * Version 1 - July 25, 2006 * * * gcc -O -shared -I/usr/local/include -DUSE_TCL_STUBS rpn-5.c \ /usr/local/lib/libtclstub8.6.a -Wall -o rpn.so * * gcc -O -Wall -finline-functions -Winline -DUSE_TCL_STUBS -std=c99 rpn-3.c \ -I/gps/runtime8.4/include -L/gps/runtime8.4/lib -ltclstub84 -shared -o rpn.so * * when optimising: gcc -O -Wall -finline-functions -Winline -DUSE_TCL_STUBS -std=c99 rpn-3.c \ -I/gps/runtime8.4/include -S -fverbose-asm */ #include <assert.h> #include <tcl.h> #define OBJ_CMD_ARGS ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] static const Tcl_ObjType *rpnwidetype = NULL; static const Tcl_ObjType *rpnfloattype = NULL; static const Tcl_ObjType *rpnlongtype = NULL; static Tcl_ObjType rpnoptype = { "rpnop", NULL, NULL, NULL, NULL }; /* * The stack grows downwards from stackupperlimit. */ #define OP2_STACK_CHECK(op) do { \ if((sptr + 1) >= stackupperlimit) { \ Tcl_SetResult(interp, \ "stack underflow with " op " operator", TCL_STATIC); \ return TCL_ERROR; \ } \ } while(0) inline static int get_op_from_obj(Tcl_Interp *interp, Tcl_Obj *obj, int *op) { int slen; char *s; if(&rpnoptype == obj->typePtr) { *op = obj->bytes[0]; return TCL_OK; } s = Tcl_GetStringFromObj(obj, &slen); if(1 != slen) { Tcl_Obj *err = Tcl_ObjPrintf("invalid operator: %s", obj); Tcl_SetObjResult(interp, err); return TCL_ERROR; } if(NULL != obj->typePtr && NULL != obj->typePtr->freeIntRepProc) { obj->typePtr->freeIntRepProc(obj); } obj->typePtr = &rpnoptype; *op = obj->bytes[0]; return TCL_OK; } /* * This macro is used to generate at least 3 functions for handling * different types. */ #define TEMPLATE(FUNC,ARGS,STACKTYPE,GET,NEWOBJ,OBJTYPE) \ static int FUNC ( ARGS ) { \ STACKTYPE stack[objc], *sptr, *stackupperlimit, value; \ int i; \ int op; \ \ if(objc <= 1) { \ Tcl_WrongNumArgs(interp, 1, objv, "n ?n? ?op? ..."); \ return TCL_ERROR; \ } \ \ stackupperlimit = sptr = stack + objc; \ \ for(i = 1; i < objc; ++i) { \ Tcl_Obj *obj = objv[i]; \ if(TCL_OK == GET(NULL, obj, &value)) { \ --sptr; *sptr = value; \ continue; \ } \ \ if(TCL_OK != get_op_from_obj(interp, obj, &op)) { \ return TCL_ERROR; \ } \ \ switch(op) { \ case '+': \ OP2_STACK_CHECK("+"); \ *(sptr + 1) += *sptr; \ ++sptr; \ break; \ \ case '-': \ OP2_STACK_CHECK("-"); \ *(sptr + 1) -= *sptr; \ ++sptr; \ break; \ \ case '*': \ OP2_STACK_CHECK("*"); \ *(sptr + 1) *= *sptr; \ ++sptr; \ break; \ \ case '/': \ OP2_STACK_CHECK("/"); \ *(sptr + 1) /= *sptr; \ ++sptr; \ break; \ \ default: { \ char opstr[2]; \ Tcl_Obj *err; \ opstr[0] = op; \ opstr[1] = '\0'; \ \ err = Tcl_ObjPrintf("invalid operator: %s", opstr); \ Tcl_SetObjResult(interp, err); \ return TCL_ERROR; \ } \ break; \ } \ } \ \ if((stackupperlimit - 1) != sptr) { \ Tcl_SetResult(interp, \ "more than 1 value remains on the operand stack", \ TCL_STATIC); \ return TCL_ERROR; \ } \ Tcl_SetObjResult(interp, NEWOBJ (*sptr)); \ \ return TCL_OK; \ } TEMPLATE(rpnwide_cmd, OBJ_CMD_ARGS, Tcl_WideInt, Tcl_GetWideIntFromObj, Tcl_NewWideIntObj, rpnwidetype); TEMPLATE(rpnfloat_cmd, OBJ_CMD_ARGS, double, Tcl_GetDoubleFromObj, Tcl_NewDoubleObj, rpnfloattype); TEMPLATE(rpn_cmd, OBJ_CMD_ARGS, long, Tcl_GetLongFromObj, Tcl_NewLongObj, rpnlongtype); int Rpn_Init (Tcl_Interp *interp) { Tcl_Obj *tmp; if (NULL == Tcl_InitStubs (interp, TCL_VERSION, 0)) return TCL_ERROR; tmp = Tcl_NewWideIntObj (1); rpnwidetype = tmp->typePtr; Tcl_DecrRefCount (tmp); tmp = Tcl_NewDoubleObj (1.0); rpnfloattype = tmp->typePtr; Tcl_DecrRefCount (tmp); tmp = Tcl_NewLongObj (1); rpnlongtype = tmp->typePtr; Tcl_DecrRefCount (tmp); Tcl_CreateObjCommand (interp, "rpnwide", rpnwide_cmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand (interp, "rpnfloat", rpnfloat_cmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand (interp, "rpn", rpn_cmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); return TCL_OK; } /* * Local Variables: * mode: c * c-basic-offset: 8 * fill-column: 78 * End: */