diff --git a/examples/custom/01-parameter-passing/cf_demo1.c b/examples/custom/01-parameter-passing/cf_demo1.c new file mode 100644 index 0000000..74708cc --- /dev/null +++ b/examples/custom/01-parameter-passing/cf_demo1.c @@ -0,0 +1,128 @@ +#include "pf_all.h" /* lots of stuff */ +#include "cf_helpers.h" /* to_C_string */ +#include /* errno */ +#include /* PATH_MAX */ +#include /* malloc */ +#include /* asprintf, sprintf */ +#include /* strerror */ +#include /* struct stat, stat */ + +/* + * put forward declarations here if necessary +*/ + + +/**************************************************************** +** Step 1: Put your own special glue routines here +** or link them in from another file or library. +****************************************************************/ + +/* exported functions */ + +static cell_t f4711( cell_t Val ) +{/* a quick way to check that custom words are available + */ + return 11 + 47*Val; +} + +static cell_t file_info( cell_t path_caddr, cell_t path_len ) +{ + /* takes one filePath (string) as argument and returns some info on it (as a new string) + Note that you need to use FREE-C on the result buffer (FREE does not work). + */ + char* path = to_C_string( path_caddr, path_len ); + struct stat info; + const char* fmtErr = "error{ id=%i, desc='%s', path='%s' }"; + const char* fmtDir = "directory{ path='%s' }"; + const char* fmtFile = "file{ size=%i, path='%s' }"; + char* result; + cell_t res_len = -1; + /* MSYS/Cygwin may warn than asprintf() is not defined but compile and run just fine :-/ */ + if( stat(path, &info) == -1 ) + res_len = asprintf( &result, fmtErr, errno, strerror(errno), path ); + else { + if( S_ISDIR(info.st_mode) ) + res_len = asprintf( &result, fmtDir, path ); + else + res_len = asprintf( &result, fmtFile, info.st_size, path ); + } + PUSH_DATA_STACK( (cell_t) result ); + return res_len; +} + +static void free_c( cell_t c_allocate_buffer ) +{ + /* Using FREE on a c_allocate_buffer is not possible (due to extra information stored), + using pfAllocMem() / pfFreeMem() does not help either. + So we need a separate word to free C-allocated buffers. + */ + free( (void*) c_allocate_buffer ); +} + + +/**************************************************************** +** Step 2: Create CustomFunctionTable. +** Do not change the name of CustomFunctionTable! +** It is used by the pForth kernel. +****************************************************************/ + +#ifdef PF_NO_GLOBAL_INIT +/****************** +** If your loader does not support global initialization, then you +** must define PF_NO_GLOBAL_INIT and provide a function to fill +** the table. Some embedded system loaders require this! +** Do not change the name of LoadCustomFunctionTable()! +** It is called by the pForth kernel. +*/ +#define NUM_CUSTOM_FUNCTIONS (3) +CFunc0 CustomFunctionTable[NUM_CUSTOM_FUNCTIONS]; + +Err LoadCustomFunctionTable( void ) +{ + CustomFunctionTable[0] = f4711; + CustomFunctionTable[1] = file_info; + CustomFunctionTable[1] = free_c; + return 0; +} + +#else +/****************** +** If your loader supports global initialization (most do.) then just +** create the table like this. +*/ +CFunc0 CustomFunctionTable[] = +{ + (CFunc0) f4711, + (CFunc0) file_info, + (CFunc0) free_c +}; +#endif + + +/**************************************************************** +** Step 3: Add custom functions to the dictionary. +** Do not change the name of CompileCustomFunctions! +** It is called by the pForth kernel. +****************************************************************/ + +#if (!defined(PF_NO_INIT)) && (!defined(PF_NO_SHELL)) +Err CompileCustomFunctions( void ) +{ + Err err; + int i = 0; +/* Compile Forth words that call your custom functions. +** Make sure order of functions matches that in LoadCustomFunctionTable(). +** Parameters are: Name in UPPER CASE, Function, Index, Mode, NumParams +*/ + err = CreateGlueToC( "F4711" , i++, C_RETURNS_VALUE, 1 ); + if( err < 0 ) return err; + err = CreateGlueToC( "FILE-INFO", i++, C_RETURNS_VALUE, 2 ); + if( err < 0 ) return err; + err = CreateGlueToC( "FREE-C" , i++, C_RETURNS_VOID , 1 ); + if( err < 0 ) return err; + + return 0; +} +#else +Err CompileCustomFunctions( void ) { return 0; } +#endif diff --git a/examples/custom/01-parameter-passing/demo.fth b/examples/custom/01-parameter-passing/demo.fth new file mode 100644 index 0000000..c618244 --- /dev/null +++ b/examples/custom/01-parameter-passing/demo.fth @@ -0,0 +1,19 @@ +\ f4711 is a clear indicator that compilation of custom functions was successful +." f4711( 0, 1, 10, 100, 1000 ) = ( " + 0 f4711 . ." , " + 1 f4711 . ." , " + 10 f4711 . ." , " + 100 f4711 . ." , " +1000 f4711 . ." )" +CR + +\ example of passing strings from PForth to custom C code +: SHOW-FILE-INFO + FILE-INFO over -rot + ." " type cr + free-c + ; +." FILE-INFO: " cr +s" Makefile" SHOW-FILE-INFO +s" ../../examples" SHOW-FILE-INFO +s" fileNotHere" SHOW-FILE-INFO diff --git a/examples/custom/01-parameter-passing/go.sh b/examples/custom/01-parameter-passing/go.sh new file mode 100644 index 0000000..df4a4a7 --- /dev/null +++ b/examples/custom/01-parameter-passing/go.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +# Compile pForth with custom code and show that this works. +# We assume a posix shell and system (but adaption should be easy to others). +# This improved version only compiles the custom code defined in CF_SOURCES. +# Warning: This patches the existing source tree and might create confusion when not used on separate Git branch in case an error occurs. +# Tested on MSYS2-Cygwin, Linux, FreeBSD (X86_64 architecture each), NetBSD (i386 architecture) + +# copy demo sources. Thus we do not need to change the make file. + +cp ../cf_helpers.h ../../../csrc/ +cp cf_demo1.c ../../../csrc/ +CUSTOM_SOURCES="cf_demo1.c" +export CUSTOM_SOURCES + +echo +echo "----------------------------------------" +echo "make pforth (skip standalone executable)" +echo "----------------------------------------" +MAKE_CMD=`../get-make-cmd.sh` +cd ../../../platforms/unix/ + +$MAKE_CMD pforth.dic # we just need a PForth executable+dictionary + +echo +echo "---------------------------" +echo "show that custom code works" +echo "---------------------------" +./pforth -q ../../examples/custom/01-parameter-passing/demo.fth + +echo +echo "----------------------------" +echo "restore original source tree" +echo "----------------------------" +rm ../../csrc/cf_helpers.h +rm ../../csrc/cf_demo1.c +$MAKE_CMD clean + +echo +echo "-----------------" +echo "That's all folks!" +echo "-----------------" diff --git a/examples/custom/cf_helpers.h b/examples/custom/cf_helpers.h new file mode 100644 index 0000000..f0c224d --- /dev/null +++ b/examples/custom/cf_helpers.h @@ -0,0 +1,26 @@ +/* custom code for pforth (hence Custom Forth = cf) + This is a hack and for demonstration purposes only. + It simplifies a few things (like patching of Makefile) + but violates rules for production C code (e.g placing definitions in header files and terminating at the 1st sign of trouble). + Defines helper functions for several examples. +*/ + +#ifndef CF_HELPERS_H +#define CF_HELPERS_H + +#include /* malloc */ +#include /* memcpy */ + +static char* to_C_string( cell_t strData, cell_t iStrLen ) +{/* copy PForth string to C-string (zero terminated) + Don't forget to free() the result! + */ + char* buf = malloc(iStrLen+1); + if( buf != NULL ) { + memcpy( buf, (void*)strData, iStrLen ); + buf[iStrLen] = 0; + } + return buf; +} + +#endif /* CF_HELPERS_H */ diff --git a/examples/custom/get-make-cmd.sh b/examples/custom/get-make-cmd.sh new file mode 100644 index 0000000..135f9f4 --- /dev/null +++ b/examples/custom/get-make-cmd.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +os=`uname -o 2>/dev/null` +if test -z "$os" ; then + # NetBSD-uname does not implement '-o' option + os=`uname -s` +fi + +case "$os" in + "FreeBSD" | "NetBSD") + echo "gmake" + ;; + *) # e.g. "Msys" | "GNU/Linux" + echo "make" + ;; +esac diff --git a/platforms/unix/Makefile b/platforms/unix/Makefile index f4dfa9d..400b22b 100644 --- a/platforms/unix/Makefile +++ b/platforms/unix/Makefile @@ -59,6 +59,13 @@ FULL_WARNINGS = \ -Wmissing-prototypes \ -Wmissing-declarations +# custom code options +ifndef CUSTOM_SOURCES + # note: setting CF_PARAM does not help: It only deactivates all definitions in pfcustom.c, which we only include when no custom code is compiled! + # We do not remove CF_PARAM from code and documentation until we can verify that Makefiles continue to work. + CUSTOM_SOURCES=pfcustom.c +endif + DEBUGOPTS = -g CCOPTS = $(WIDTHOPT) -x c -O2 $(FULL_WARNINGS) $(EXTRA_CCOPTS) $(DEBUGOPTS) @@ -82,7 +89,7 @@ PFINCLUDES = pf_all.h pf_cglue.h pf_clib.h pf_core.h pf_float.h \ pfcompil.h pfinnrfp.h pforth.h PFBASESOURCE = pf_cglue.c pf_clib.c pf_core.c pf_inner.c \ pf_io.c pf_io_none.c pf_main.c pf_mem.c pf_save.c \ - pf_text.c pf_words.c pfcompil.c pfcustom.c + pf_text.c pf_words.c pfcompil.c $(CUSTOM_SOURCES) PFSOURCE = $(PFBASESOURCE) $(IO_SOURCE) VPATH = .:$(CSRCDIR):$(CSRCDIR)/posix:$(CSRCDIR)/stdio:$(CSRCDIR)/win32_console:$(CSRCDIR)/win32