package ph7 /* #include "cgo.h" #include "ph7.h" */ import "C" import ( "fmt" "io" "unsafe" pointer "github.com/mattn/go-pointer" ) type ResultCode int const ( ResultCodeOK = ResultCode(C.PH7_OK) ResultCodeNoMem = ResultCode(C.PH7_NOMEM) ResultCodeAbort = ResultCode(C.PH7_ABORT) ResultCodeIOErr = ResultCode(C.PH7_IO_ERR) ResultCodeLooked = ResultCode(C.PH7_LOOKED) ResultCodeCorrupt = ResultCode(C.PH7_CORRUPT) ResultCodeCompileErr = ResultCode(C.PH7_COMPILE_ERR) ResultCodeVMErr = ResultCode(C.PH7_VM_ERR) ) func (r ResultCode) String() string { switch r { case ResultCodeOK: return "PH7_OK" case ResultCodeNoMem: return "PH7_NOMEM" case ResultCodeAbort: return "PH7_ABORT" case ResultCodeIOErr: return "PH7_IO_ERR" case ResultCodeLooked: return "PH7_LOOKED" case ResultCodeCorrupt: return "PH7_CORRUPT" case ResultCodeCompileErr: return "PH7_COMPILE_ERR" case ResultCodeVMErr: return "PH7_VM_ERR" default: return "UNKNOWN" } } type Error struct { Code ResultCode } // TODO create a CompileError that contains the error log text func newError(code C.int) error { if code != C.PH7_OK { return Error{ Code: ResultCode(code), } } return nil } func (e Error) Error() string { return fmt.Sprintf("ph7: %s", e.Code) } type Engine C.ph7 // TODO strip off the enclosing struct if possible after wrapping all engine methods. func NewEngine() (*Engine, error) { var cengine *C.ph7 result := C.ph7_init(&cengine) if result != C.PH7_OK { return nil, newError(result) } return (*Engine)(cengine), nil } func (e *Engine) ErrLog() (string, error) { var s *C.char var n C.int err := newError(C.engine_config_err_log((*C.ph7)(e), &s, &n)) if err != nil { return "", err } return C.GoStringN(s, n), nil } func (e *Engine) Compile(source []byte, phpOnly bool) (*VM, error) { csource := C.CBytes(source) defer C.free(csource) var flags C.int if phpOnly { flags |= C.PH7_PHP_ONLY } var cvm *C.ph7_vm err := newError(C.ph7_compile_v2((*C.ph7)(e), (*C.char)(csource), C.int(len(source)), &cvm, flags)) if err != nil { return nil, err } return newVM(cvm), nil } func (e *Engine) Close() error { return newError(C.ph7_release((*C.ph7)(e))) } type Value C.ph7_value type Context C.ph7_context type VM struct { ptr *C.ph7_vm pointerKey unsafe.Pointer outputWriter io.Writer // TODO clear outputWriteError when resetting VM outputWriteErr error userFunctionKeys []unsafe.Pointer } func newVM(cvm *C.ph7_vm) *VM { vm := new(VM) vm.ptr = cvm vm.pointerKey = pointer.Save(vm) return vm } //export write_vm_output func write_vm_output(buf unsafe.Pointer, bufLen C.uint, userData unsafe.Pointer) C.int { vm := pointer.Restore(userData).(*VM) gobuf := C.GoBytes(buf, C.int(bufLen)) _, vm.outputWriteErr = vm.outputWriter.Write(gobuf) if vm.outputWriteErr != nil { return C.int(ResultCodeAbort) } return C.int(ResultCodeOK) } func (vm *VM) SetOutputWriter(w io.Writer) error { vm.outputWriter = w return newError(C.vm_set_output_callback(vm.ptr, vm.pointerKey)) } func (vm *VM) OutputWriteError() error { return vm.outputWriteErr } func (vm *VM) ExtractOutput() (string, error) { var s *C.char var n C.int err := newError(C.vm_extract_output(vm.ptr, &s, &n)) if err != nil { return "", err } return C.GoStringN(s, n), nil } func (vm *VM) Exec() error { // TODO return exit status return newError(C.ph7_vm_exec(vm.ptr, (*C.int)(nil))) } func (vm *VM) Reset() error { return newError(C.ph7_vm_reset(vm.ptr)) } func (vm *VM) Close() error { pointer.Unref(vm.pointerKey) for _, key := range vm.userFunctionKeys { pointer.Unref(key) } return newError(C.ph7_vm_release(vm.ptr)) } type ForeignFunction interface { Call(ctx *Context, args []*Value) ResultCode } //export call_foreign_function func call_foreign_function(ctx *C.ph7_context, argc C.int, argv **C.ph7_value) C.int { fkey := C.ph7_context_user_data(ctx) f := pointer.Restore(fkey).(ForeignFunction) args := make([]*Value, 0, argc) argvSlice := unsafe.Slice(argv, argc) for i := range argvSlice { args = append(args, (*Value)(argvSlice[i])) } return C.int(f.Call((*Context)(ctx), args)) } func (vm *VM) CreateFunction(name string, f ForeignFunction) error { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) fkey := pointer.Save(f) vm.userFunctionKeys = append(vm.userFunctionKeys, fkey) return newError(C.vm_create_function(vm.ptr, cname, fkey)) }