go-ph7/ph7.go

211 lines
4.4 KiB
Go
Raw Normal View History

2022-11-25 01:58:10 +00:00
package ph7
/*
2022-11-25 07:56:56 +00:00
#include "cgo.h"
2022-11-25 01:58:10 +00:00
#include "ph7.h"
*/
import "C"
import (
"fmt"
2022-11-25 05:19:40 +00:00
"io"
2022-11-25 07:56:56 +00:00
"unsafe"
pointer "github.com/mattn/go-pointer"
2022-11-25 01:58:10 +00:00
)
2022-11-25 04:51:44 +00:00
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"
}
2022-11-25 01:58:10 +00:00
}
2022-11-25 04:51:44 +00:00
type Error struct {
Code ResultCode
2022-11-25 01:58:10 +00:00
}
2022-11-27 04:45:16 +00:00
// TODO create a CompileError that contains the error log text
2022-11-25 04:51:44 +00:00
func newError(code C.int) error {
if code != C.PH7_OK {
return Error{
Code: ResultCode(code),
}
2022-11-25 01:58:10 +00:00
}
2022-11-25 04:51:44 +00:00
return nil
}
func (e Error) Error() string {
return fmt.Sprintf("ph7: %s", e.Code)
2022-11-25 01:58:10 +00:00
}
2022-11-27 15:30:53 +00:00
type Engine C.ph7
// TODO strip off the enclosing struct if possible after wrapping all engine methods.
2022-11-25 01:58:10 +00:00
2022-11-27 15:30:53 +00:00
func NewEngine() (*Engine, error) {
var cengine *C.ph7
result := C.ph7_init(&cengine)
2022-11-25 01:58:10 +00:00
if result != C.PH7_OK {
2022-11-27 15:30:53 +00:00
return nil, newError(result)
2022-11-25 01:58:10 +00:00
}
2022-11-27 15:30:53 +00:00
return (*Engine)(cengine), nil
2022-11-25 01:58:10 +00:00
}
2022-11-27 15:30:53 +00:00
func (e *Engine) ErrLog() (string, error) {
2022-11-25 01:58:10 +00:00
var s *C.char
var n C.int
2022-11-27 15:30:53 +00:00
err := newError(C.engine_config_err_log((*C.ph7)(e), &s, &n))
2022-11-25 04:51:44 +00:00
if err != nil {
return "", err
2022-11-25 01:58:10 +00:00
}
return C.GoStringN(s, n), nil
}
2022-11-27 15:30:53 +00:00
func (e *Engine) Compile(source []byte, phpOnly bool) (*VM, error) {
2022-11-25 01:58:10 +00:00
csource := C.CBytes(source)
2022-11-25 05:19:40 +00:00
defer C.free(csource)
2022-11-25 01:58:10 +00:00
var flags C.int
if phpOnly {
flags |= C.PH7_PHP_ONLY
}
2022-11-27 15:30:53 +00:00
var cvm *C.ph7_vm
err := newError(C.ph7_compile_v2((*C.ph7)(e), (*C.char)(csource), C.int(len(source)), &cvm, flags))
2022-11-25 04:51:44 +00:00
if err != nil {
return nil, err
2022-11-25 01:58:10 +00:00
}
2022-11-27 15:30:53 +00:00
return newVM(cvm), nil
2022-11-25 01:58:10 +00:00
}
2022-11-27 15:30:53 +00:00
func (e *Engine) Close() error {
return newError(C.ph7_release((*C.ph7)(e)))
2022-11-25 01:58:10 +00:00
}
2022-11-27 04:45:16 +00:00
type Value C.ph7_value
type Context C.ph7_context
2022-11-25 01:58:10 +00:00
type VM struct {
2022-11-25 07:56:56 +00:00
ptr *C.ph7_vm
pointerKey unsafe.Pointer
outputWriter io.Writer
// TODO clear outputWriteError when resetting VM
2022-11-27 04:45:16 +00:00
outputWriteErr error
userFunctionKeys []unsafe.Pointer
2022-11-25 01:58:10 +00:00
}
2022-11-25 07:56:56 +00:00
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
2022-11-25 05:19:40 +00:00
}
2022-11-25 07:56:56 +00:00
func (vm *VM) ExtractOutput() (string, error) {
2022-11-25 01:58:10 +00:00
var s *C.char
var n C.int
2022-11-25 04:51:44 +00:00
err := newError(C.vm_extract_output(vm.ptr, &s, &n))
if err != nil {
return "", err
2022-11-25 01:58:10 +00:00
}
return C.GoStringN(s, n), nil
}
2022-11-25 07:56:56 +00:00
func (vm *VM) Exec() error {
2022-11-26 23:30:16 +00:00
// TODO return exit status
2022-11-25 07:56:56 +00:00
return newError(C.ph7_vm_exec(vm.ptr, (*C.int)(nil)))
2022-11-25 01:58:10 +00:00
}
2022-11-26 23:30:16 +00:00
func (vm *VM) Reset() error {
return newError(C.ph7_vm_reset(vm.ptr))
}
2022-11-25 07:56:56 +00:00
func (vm *VM) Close() error {
pointer.Unref(vm.pointerKey)
2022-11-27 04:45:16 +00:00
for _, key := range vm.userFunctionKeys {
pointer.Unref(key)
}
2022-11-25 07:56:56 +00:00
return newError(C.ph7_vm_release(vm.ptr))
2022-11-25 01:58:10 +00:00
}
2022-11-27 04:45:16 +00:00
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))
}