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-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
|
|
|
}
|
|
|
|
|
|
|
|
type Engine struct {
|
|
|
|
ptr *C.ph7
|
|
|
|
}
|
|
|
|
|
2022-11-25 04:51:44 +00:00
|
|
|
func NewEngine() (Engine, error) {
|
|
|
|
var engine Engine
|
2022-11-25 01:58:10 +00:00
|
|
|
result := C.ph7_init(&engine.ptr)
|
|
|
|
if result != C.PH7_OK {
|
2022-11-25 04:51:44 +00:00
|
|
|
return Engine{}, newError(result)
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return engine, nil
|
|
|
|
}
|
|
|
|
|
2022-11-25 04:51:44 +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-25 04:51:44 +00:00
|
|
|
err := newError(C.engine_config_err_log(e.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 04:51:44 +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-25 07:56:56 +00:00
|
|
|
var ptr *C.ph7_vm
|
|
|
|
err := newError(C.ph7_compile_v2(e.ptr, (*C.char)(csource), C.int(len(source)), &ptr, 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-25 07:56:56 +00:00
|
|
|
return newVM(ptr), nil
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-25 04:51:44 +00:00
|
|
|
func (e Engine) Close() error {
|
|
|
|
return newError(C.ph7_release(e.ptr))
|
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
|
|
|
|
outputWriteErr error
|
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)
|
|
|
|
return newError(C.ph7_vm_release(vm.ptr))
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|