Compare commits

..

101 Commits

Author SHA1 Message Date
Michael Uleysky 6c6b52f5b2 Gmt module: Fix bad assignment to uninitialised pointer in previous commit. 8 years ago
Michael Uleysky a47ff9fdcc Gmt module: Fix warnings of gcc6. 8 years ago
Michael Uleysky 995da9cf3b Gmt module: regenerate more compact hash table for color names. 8 years ago
Michael Uleysky 0b4ed61542 Gmr module: Add function ToModel in gmt_color structure. 8 years ago
Michael Uleysky 4709ebe1fb Gmt module: Functions Map, Convert2PNG and Convert2JPEG now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky 6a70f5ffbf Gmt module: Function DrawFrame now using new parameter reading scheme and error reporting 8 years ago
Michael Uleysky 0a92d40792 Gmt module: Function LayerShift now using new parameter reading scheme and error reporting 8 years ago
Michael Uleysky 920a240bd0 Gmt module. Error reporting in GMT_Type template. 8 years ago
Michael Uleysky c27463ba62 Gmt module: Conversion function for GMTFont now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky 750b1aa8ce Gmt module: Remove another std::move from Parameter class. 8 years ago
Michael Uleysky 8d843a7607 Gmt module: Fix error in gmt_font::Convert(). 8 years ago
Michael Uleysky cbfd462ff0 Gmt module: Conversion function for GMTPen now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky d02fd289f8 Gmt module: Functions ColorGray, ColorRGB, ColorHSV, ColorCMYK now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky 591bc837eb Gmt module: Fix error in conversion function from string to gmt_dash. 8 years ago
Michael Uleysky b3c628f704 Tests: Add test for creating different GMTProjection's. 8 years ago
Michael Uleysky 55eef96865 Gmt module: Rename incorrect template name Base2NonNeg to right Base2Pos. 8 years ago
Michael Uleysky 41d13f16bd Gmt module: Conversion function for GMTColor now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky a3a6fa88f9 Gmt module: Error reporting in all Convert functions. 8 years ago
Michael Uleysky 59dfac220a Gmt module: Conversion function for GMTProjection now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky 5b466460d6 Gmt module: Conversion function for GMTRegion now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky 8a088d14c3 Gmt module: Rewrite Conver2Struct and its derivatives in form appropriate for new parameters parsing scheme. 8 years ago
Michael Uleysky e485d26369 Gmt module. Add error reporting in function Convert of gmt_coord. 8 years ago
Michael Uleysky 6e9dfbf0e7 Gmt module: Using template-based projections names. Fix errors in string representations of projections. 8 years ago
Michael Uleysky 218d8c0f00 Gmt module: Add possibility for ParsePositionalParameters to parse only part of list. 8 years ago
Michael Uleysky 3d1f1ea499 Gmt module: Fix segfault. 8 years ago
Michael Uleysky 3be794e42c Gmt module: Simplify code. 8 years ago
Michael Uleysky 8a24425a16 Gmt module: generate parameter name from template. 8 years ago
Michael Uleysky 4578ce31ad Gmt module: Add templates and classes for parsing parameters from ObjectList. 8 years ago
Michael Uleysky 9c0776b92e Gmt module: Cleanup modgmt_structs.h 8 years ago
Michael Uleysky 4e9addae93 Simple tests system. 8 years ago
Michael Uleysky c59ac16589 Fix false error if more then one function have same name. 8 years ago
Michael Uleysky 5827eebde3 Add header guard in builtin.h. 8 years ago
Michael Uleysky a48e2f9d42 Gmt module: Fix compilation on Ubuntu. 8 years ago
Michael Uleysky afc5ae64d8 Gmt module: Replace ObjectList::ListValues::size_type on ObjectList::IndexType. 8 years ago
Michael Uleysky c02f46df5e Using ObjectList::IndexType instead of ObjectList::ListValues::size_type. 8 years ago
Michael Uleysky 3ec43c7de5 Gmt module: Add class for comparision strings with templates. 8 years ago
Michael Uleysky 522a4b2092 Remove unused mutexes from DepTree::thread_params. 8 years ago
Michael Uleysky b2a7e3627c Remove unneeded functions from ObjectPair and ObjectList classes. 8 years ago
Michael Uleysky 18911f66b5 Gmt module: all functions return const ObjectBase* 8 years ago
Michael Uleysky acc973982a Enforce constantness of all Objects. 8 years ago
Michael Uleysky da0c897a95 Error reporting in arifmetic and Get<> functions. 8 years ago
Michael Uleysky 79c55b1618 Add support for extended error reporting by functions (functions can return ObjectError object with description of error). 8 years ago
Michael Uleysky 0e49a86bd6 Rewrite parser and evaluator. 8 years ago
Michael Uleysky eeeb74f2f4 Error messages from lexical and grammatical parsers now contains include stack. 8 years ago
Michael Uleysky e1443f1636 Gmt module: Add functions Convert2PNG and Convert2JPG. 9 years ago
Michael Uleysky 64e4613c67 Gmt module: Add function Convert2PDF. 9 years ago
Michael Uleysky b3c59f832d Gmt module: Fix memory leak, pointer returned by Get() must be deleted, so, use Find() instead. 9 years ago
Michael Uleysky 176bdb2be0 New function Find() for ObjectPair and ObjectList. Same as Get() but return constant pointer on already existing object. 9 years ago
Michael Uleysky f34ab7676c Gmt module: Add function Map. 9 years ago
Michael Uleysky 602fcdf675 Unset CMAKE_SHARED_MODULE_PREFIX to prevent building modules as lib<name>.so. Modules names are <name>.so. 9 years ago
Michael Uleysky 8802277bdb Gmt module: Add interface to ghostscript. 9 years ago
Michael Uleysky 09b2684d4b Gmt module: Search and use libgs. 9 years ago
Michael Uleysky d458087d33 Gmt module: Function GMT_DrawFrame for drawing map frame with ticks and annotations. 9 years ago
Michael Uleysky 7fcf2ee526 Ignore kdevelop files in .gitignore. 9 years ago
Michael Uleysky 5c5e722d53 Gmt module: Rewrite template structure. 9 years ago
Michael Uleysky c3c6f4643d Use ToString for printing and dumping ObjectSimple classes. 9 years ago
Michael Uleysky 79ab3b72a7 Gmt module: Fix potential segmentation fault. 9 years ago
Michael Uleysky 4df0d70633 Gmt module: Make ObjectGMTLayer class via ObjectGMTClass template. Add function GMT_LayerShift. 9 years ago
Michael Uleysky b710786dab Gmt module: Add BaseM2Double for easy access to parameters with multiple names. 9 years ago
Michael Uleysky 1197c50567 Gmt module: Add structure and function for representation of font parameters. 9 years ago
Michael Uleysky 78e9176d0b Gmt module: Use ToString instead of std::to_string 9 years ago
Michael Uleysky 05ba7c2421 Gmt module: Rewrite modgmt_func in more template way. 9 years ago
Michael Uleysky 8c2f0c6ef8 Gmt module: Add function Pen. 9 years ago
Michael Uleysky 07752e21c9 Gmt module: Make Base2Something slightly more generic. 9 years ago
Michael Uleysky 9dec1223ca Gmt module: Rename all files to begin with modgmt_ 9 years ago
Michael Uleysky db9f8b1a93 Gmt module: Functions Color, ColorGray, ColorRGB, ColorHSV, and ColorCMYK. 9 years ago
Michael Uleysky f1d960a769 Gmt module: Base2Something raise error flag if initialized by pointer of incompatible type. 9 years ago
Michael Uleysky 30e3eaeadc Gmt module: Prevent conflict of definitions of yyguts_t from different lex files. 9 years ago
Michael Uleysky 5973d82042 Gmt module: Transformation to lowercase was not used then setting projection by name. 9 years ago
Michael Uleysky 07f43394f4 Gmt module: Place const qualifiers where needed. 9 years ago
Michael Uleysky 86ecc17747 Gmt module: Change logic of Projection and Region function. 9 years ago
Michael Uleysky ff4503585c Gmt module: Using Split instead of manual splitting. 9 years ago
Michael Uleysky fbb0c9a9ed Gmt module: Realization of Get functions for GMT objects. 9 years ago
Michael Uleysky 6267df2b4b Gmt module: In struct gmt_projection: 9 years ago
Michael Uleysky 86456cb5dd Gmt module: ObjectBase-based classes for gmt module. 9 years ago
Michael Uleysky 772cf30db2 Gmt module: Structures, representing GMT entities. 9 years ago
Michael Uleysky c0d77b160d Gmt module: CMake build system for gmt module 9 years ago
Michael Uleysky db62ae83ec Gmt module: Filters in callgmtmodule. Add default filter and lex-based filter for cutting header and footer of generated eps files. 9 years ago
Michael Uleysky b0a801a855 Gmt module: Gmt module initial commit 9 years ago
Michael Uleysky 5703808781 Using ToString instead of std::to_string with different format conversion for double 9 years ago
Michael Uleysky 235437354e Check and really use linker flags 9 years ago
Michael Uleysky fc13d6586a Fix incorrect parameter type. 9 years ago
Michael Uleysky 92a617eaac Prevent conflict of definition of yyguts_t from different lex files. 9 years ago
Michael Uleysky 360cbd42a0 Move const qualifier in right place. 9 years ago
Michael Uleysky 8e9d4c3f0e Function Split to break strings by delimiters. 9 years ago
Michael Uleysky 90f6c74c0a Fix incorrect check. 9 years ago
Michael Uleysky 403b2edb62 Add function Exist() to temaplates OBType and OBTypeM to check if ObjectBase* pointer is non-zero. 9 years ago
Michael Uleysky 186017f05c Check compiler options. 9 years ago
Michael Uleysky b0969362a7 OBType and OBTypeM now can accept zero pointer as constructor parameter. 9 years ago
Michael Uleysky c218ef8eaf Use templates for generating standart arifmetic functions. 9 years ago
Michael Uleysky c4ff7d9233 Add template OBTypeM as multiple-types analog of OBType. 9 years ago
Michael Uleysky 3ef04a9ba0 Add tolower function for strings. 9 years ago
Michael Uleysky 5bed99ad81 Simple converters from strings to double, int and unsigned int added to common.h 9 years ago
Michael Uleysky 0a4dbbadc7 Change type of pointer argument of the function DeallocateBlob() from void* to int8_t*, as Blob() return value. 9 years ago
Michael Uleysky b442834bdc Make template Get available for modules. 9 years ago
Michael Uleysky 162a18c72b Replace IS_OTYPE macros on more useful template OBType 9 years ago
Michael Uleysky 60e557a711 Disable CMake in-source build 9 years ago
Michael Uleysky ebc4f236db CMake build system 9 years ago
Michael Uleysky c63bbdc2ab Let modules to change list of source files. 9 years ago
Michael Uleysky 71a5ea4272 More strict .gitignore 9 years ago
Michael Uleysky c5bff0c2f0 Add externally defined CFLAGS and LDFLAGS to compiler and linker options. 9 years ago
  1. 15
      .gitignore
  2. 126
      CMakeLists.txt
  3. 14
      Makefile
  4. 11
      cmake/modules/BuildModule.cmake
  5. 114
      cmake/modules/FindGMT.cmake
  6. 34
      dotests
  7. 127
      include/builtin.h
  8. 439
      include/common.h
  9. 18
      include/deptree.h
  10. 15
      include/globals.h
  11. 5
      include/init.h
  12. 145
      include/object.h
  13. 14
      include/parser.h
  14. 34
      maketest
  15. 2
      modules/gmt/.gitignore
  16. 3
      modules/gmt/CMakeLists.txt
  17. 24
      modules/gmt/ModuleSetup.cmake
  18. 35
      modules/gmt/makemod
  19. 58
      modules/gmt/modgmt.cpp
  20. 9
      modules/gmt/modgmt.h
  21. 1277
      modules/gmt/modgmt_colornames.cpp
  22. 84
      modules/gmt/modgmt_colornames.h
  23. 135
      modules/gmt/modgmt_colortransform.h
  24. 46
      modules/gmt/modgmt_filter_headfoot.l
  25. 31
      modules/gmt/modgmt_filters.cpp
  26. 11
      modules/gmt/modgmt_filters.h
  27. 308
      modules/gmt/modgmt_func.cpp
  28. 1064
      modules/gmt/modgmt_func.h
  29. 168
      modules/gmt/modgmt_gsfuncs.cpp
  30. 42
      modules/gmt/modgmt_gsfuncs.h
  31. 135
      modules/gmt/modgmt_internals.cpp
  32. 52
      modules/gmt/modgmt_internals.h
  33. 460
      modules/gmt/modgmt_map.cpp
  34. 112
      modules/gmt/modgmt_map.h
  35. 97
      modules/gmt/modgmt_objects.cpp
  36. 138
      modules/gmt/modgmt_objects.h
  37. 297
      modules/gmt/modgmt_param.h
  38. 139
      modules/gmt/modgmt_strcomp.cpp
  39. 98
      modules/gmt/modgmt_strcomp.h
  40. 900
      modules/gmt/modgmt_structs.h
  41. 43
      src/CMakeLists.txt
  42. 8
      src/Makefile
  43. 155
      src/builtin.cpp
  44. 353
      src/deptree.cpp
  45. 86
      src/globals.cpp
  46. 73
      src/init.cpp
  47. 42
      src/main.cpp
  48. 230
      src/object.cpp
  49. 103
      src/parser/grammatical.y
  50. 42
      src/parser/lexical.l
  51. 32
      test/Makefile
  52. 99
      test/gmttest.cpp
  53. 11
      tests/GMTCoord
  54. 8
      tests/GMTCoord_e1
  55. 18
      tests/GMTProjection
  56. 10
      tests/GMTRegion1
  57. 9
      tests/GMTRegion2
  58. 13
      tests/GMTRegion3
  59. 8
      tests/GMTRegion_e1

15
.gitignore vendored

@ -1,8 +1,11 @@
*.[od] *.[od]
*.so *.so
include/grammatical.h *.kdev4
src/parser/grammatical.cpp .kdev4/*
include/lexical.h /include/grammatical.h
src/parser/lexical.cpp /src/parser/grammatical.cpp
src/makemap /include/lexical.h
modules/test/* /src/parser/lexical.cpp
/src/makemap
/bin/*
/modules/test/*

126
CMakeLists.txt

@ -0,0 +1,126 @@
cmake_minimum_required(VERSION 2.8.5)
# Make sure the user doesn't play dirty with symlinks
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
# Disallow in-source builds
if(${srcdir} STREQUAL ${bindir})
message(FATAL_ERROR "In-source builds are not allowed."
" Please create a directory and run cmake from there, passing the path"
" to this source directory as the last argument. This process created"
" the file `CMakeCache.txt' and the directory `CMakeFiles' in ${srcdir}."
" Please remove them.")
endif()
project(gmt_makemap CXX)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel" FORCE)
endif()
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/" CACHE INTERNAL "Location of our custom CMake modules." FORCE)
include_directories(include)
# Checking compiler options
set(default_options -Wall)
set(linker_options -Wall)
include(CheckCXXCompilerFlag)
set(saved_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
# C++11 is required
CHECK_CXX_COMPILER_FLAG(-std=c++11 COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG(-std=c++0x COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(default_options ${default_options} -std=c++11)
elseif(COMPILER_SUPPORTS_CXX0X)
set(default_options ${default_options} -std=c++0x)
else()
message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
# Link time optimization check
set(CMAKE_REQUIRED_FLAGS -flto)
CHECK_CXX_COMPILER_FLAG(-flto COMPILER_SUPPORTS_FLTO)
if(COMPILER_SUPPORTS_FLTO AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
set(default_options ${default_options} -flto)
set(linker_options ${linker_options} -flto)
endif()
unset(CMAKE_REQUIRED_FLAGS)
# Clang can use lto only with gold linker
if(NOT COMPILER_SUPPORTS_FLTO)
set(CMAKE_REQUIRED_FLAGS -flto\ -fuse-ld=gold)
CHECK_CXX_COMPILER_FLAG(-flto\ -fuse-ld=gold COMPILER_SUPPORTS_FLTO_GOLD)
if(COMPILER_SUPPORTS_FLTO_GOLD AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
set(default_options ${default_options} -flto)
set(linker_options ${linker_options} -flto\ -fuse-ld=gold)
endif()
unset(CMAKE_REQUIRED_FLAGS)
endif()
# Dwarf-4 support check
set(CMAKE_REQUIRED_FLAGS -gdwarf-4)
CHECK_CXX_COMPILER_FLAG(-gdwarf-4 COMPILER_SUPPORTS_DWARF4)
if(COMPILER_SUPPORTS_DWARF4 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
set(default_options ${default_options} -gdwarf-4)
set(linker_options ${linker_options} -gdwarf-4)
endif()
unset(CMAKE_REQUIRED_FLAGS)
# -ipo flag check (Intel compiler link time optimization)
set(CMAKE_REQUIRED_FLAGS -ipo)
CHECK_CXX_COMPILER_FLAG(-ipo COMPILER_SUPPORTS_IPO)
if(COMPILER_SUPPORTS_IPO AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
set(default_options ${default_options} -ipo)
set(linker_options ${linker_options} -ipo)
endif()
unset(CMAKE_REQUIRED_FLAGS)
# Default hidden visibility check
set(CMAKE_REQUIRED_FLAGS -fvisibility=hidden)
CHECK_CXX_COMPILER_FLAG(-fvisibility=hidden COMPILER_SUPPORTS_HIDDEN)
if(COMPILER_SUPPORTS_HIDDEN)
set(default_options ${default_options} -fvisibility=hidden)
set(linker_options ${linker_options} -fvisibility=hidden)
endif()
unset(CMAKE_REQUIRED_FLAGS)
# Export dynamic. Required as modules mast call functions from main programm
set(CMAKE_REQUIRED_FLAGS -Wl,--export-dynamic)
CHECK_CXX_COMPILER_FLAG(-Wl,--export-dynamic COMPILER_SUPPORTS_EXPORTDYNAMIC)
if(COMPILER_SUPPORTS_EXPORTDYNAMIC)
set(linker_options ${linker_options} -Wl,--export-dynamic)
else()
message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no support of --export-dynamic. Please use a different C++ compiler.")
endif()
unset(CMAKE_REQUIRED_FLAGS)
add_compile_options(${default_options})
set(CMAKE_REQUIRED_FLAGS ${saved_CMAKE_REQUIRED_FLAGS})
file(GLOB modules modules/*)
file(GLOB extramodules extramodules/*)
set(STATIC_MODULES "")
set(CMAKE_SHARED_MODULE_PREFIX "")
foreach(ext ${modules})
if(EXISTS ${ext}/CMakeLists.txt)
string(REGEX REPLACE .*/ "" modname ${ext})
option(MODULE_${modname}_BUILD "Build module ${modname}" ON)
option(MODULE_${modname}_STATIC "Link module ${modname} statically in gmt_makemap" ON)
mark_as_advanced(FORCE MODULE_${modname}_STATIC)
if(MODULE_${modname}_BUILD)
add_subdirectory(modules/${modname})
endif()
endif()
endforeach(ext)
foreach(ext ${extramodules})
if(EXISTS ${ext}/CMakeLists.txt)
string(REGEX REPLACE .*/ "" modname ${ext})
option(MODULE_${modname}_BUILD "Build module ${modname}" ON)
option(MODULE_${modname}_STATIC "Link module ${modname} statically in gmt_makemap" ON)
mark_as_advanced(FORCE MODULE_${modname}_STATIC)
if(MODULE_${modname}_BUILD)
add_subdirectory(extramodules/${modname})
endif()
endif()
endforeach(ext)
add_subdirectory(src)

14
Makefile

@ -5,15 +5,17 @@ CPPFLAGS=-std=gnu++11 -I../include -I../../include
LIBSFLAGS= LIBSFLAGS=
WARNFLAGS=-Wall WARNFLAGS=-Wall
CFLAGS=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(CPPFLAGS) CFLAGS+=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(CPPFLAGS)
LDFLAGS=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(LIBSFLAGS) LDFLAGS+=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(LIBSFLAGS)
CC=g++ CC=g++
ifdef MODULE ifdef MODULE
-include makemod
default: $(MODULE).so
SOURCE = $(wildcard *.cpp) SOURCE = $(wildcard *.cpp)
-include makemod
DEPENDS = $(subst .cpp,.d,$(SOURCE)) DEPENDS = $(subst .cpp,.d,$(SOURCE))
OBJECTS = $(subst .cpp,.o,$(SOURCE)) OBJECTS = $(subst .cpp,.o,$(SOURCE))
@ -36,11 +38,14 @@ distclean: clean
else else
default: modules
MODDIRS = $(wildcard modules/*) MODDIRS = $(wildcard modules/*)
MODDIRS+= $(wildcard extramodules/*) MODDIRS+= $(wildcard extramodules/*)
MODSOURCE = $(wildcard modules/*/*.cpp extramodules/*/*.cpp) MODSOURCE = $(wildcard modules/*/*.cpp extramodules/*/*.cpp)
MODHEADERS = $(wildcard modules/*/*.h extramodules/*/*.h) MODHEADERS = $(wildcard modules/*/*.h extramodules/*/*.h)
-include $(wildcard modules/*/makemod) $(wildcard extramodules/*/makemod)
MODOBJECTS = $(subst .cpp,.o,$(MODSOURCE)) MODOBJECTS = $(subst .cpp,.o,$(MODSOURCE))
MODDEPS = $(subst .cpp,.d,$(MODSOURCE)) MODDEPS = $(subst .cpp,.d,$(MODSOURCE))
@ -75,9 +80,6 @@ distclean:
make -C src distclean make -C src distclean
rm -f bin/* rm -f bin/*
rmdir bin rmdir bin
-include $(wildcard modules/*/makemod) $(wildcard extramodules/*/makemod)
endif endif

11
cmake/modules/BuildModule.cmake

@ -0,0 +1,11 @@
set(moddir ${CMAKE_CURRENT_SOURCE_DIR})
include(${moddir}/ModuleSetup.cmake OPTIONAL)
file(GLOB srcs *.cpp)
if(MODULE_${modname}_STATIC)
set(STATIC_MODULES ${STATIC_MODULES} ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
else()
include_directories(${MODULE_ADDITIONAL_INCLUDES})
add_library(${modname} MODULE ${srcs} ${MODULE_ADDITIONAL_SOURCES})
target_link_libraries(${modname} ${linker_options} ${MODULE_ADDITIONAL_LIBRARIES})
endif()

114
cmake/modules/FindGMT.cmake

@ -0,0 +1,114 @@
# Locate GMT
#
# This module accepts the following environment variables:
#
# GMT_DIR or GMT_ROOT - Specify the location of GMT
#
# This module defines the following CMake variables:
#
# GMT_FOUND - True if libgmt is found
# GMT_LIBRARY - A variable pointing to the GMT library
# GMT_INCLUDE_DIR - Where to find the headers
# Created by Michael Uleysky. Based on FindGDAL.cmake created by Eric Wing.
# This makes the presumption that you are include gmt.h like
#
#include "gmt.h"
if (DEFINED GMT_ROOT AND NOT GMT_ROOT)
set (GMT_LIBRARY "" CACHE INTERNAL "")
set (GMT_INCLUDE_DIR "" CACHE INTERNAL "")
return()
endif (DEFINED GMT_ROOT AND NOT GMT_ROOT)
if (UNIX AND NOT GMT_FOUND)
# Use gmt-config to obtain the library version (this should hopefully
# allow us to -lGMT5.x.y where x.y are correct version)
find_program (GMT_CONFIG gmt-config
HINTS
${GMT_DIR}
${GMT_ROOT}
$ENV{GMT_DIR}
$ENV{GMT_ROOT}
PATH_SUFFIXES bin
PATHS
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt/gmt
/opt/GMT
/opt
/usr/local
)
if (GMT_CONFIG)
execute_process (COMMAND ${GMT_CONFIG} --cflags
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE GMT_CONFIG_CFLAGS)
if (GMT_CONFIG_CFLAGS)
string (REGEX MATCHALL "-I[^ ]+" _GMT_dashI ${GMT_CONFIG_CFLAGS})
string (REGEX REPLACE "-I" "" _GMT_includepath "${_GMT_dashI}")
string (REGEX REPLACE "-I[^ ]+" "" _GMT_cflags_other ${GMT_CONFIG_CFLAGS})
endif (GMT_CONFIG_CFLAGS)
execute_process (COMMAND ${GMT_CONFIG} --libs
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE GMT_CONFIG_LIBS)
if (GMT_CONFIG_LIBS)
string (REGEX MATCHALL "-l[^ ]+" _GMT_dashl ${GMT_CONFIG_LIBS})
string (REGEX REPLACE "-l" "" _GMT_lib "${_GMT_dashl}")
string (REGEX MATCHALL "-L[^ ]+" _GMT_dashL ${GMT_CONFIG_LIBS})
string (REGEX REPLACE "-L" "" _GMT_libpath "${_GMT_dashL}")
endif (GMT_CONFIG_LIBS)
endif (GMT_CONFIG)
endif (UNIX AND NOT GMT_FOUND)
find_path (GMT_INCLUDE_DIR gmt.h
HINTS
${_GMT_includepath}
${GMT_DIR}
${GMT_ROOT}
$ENV{GMT_DIR}
$ENV{GMT_ROOT}
PATH_SUFFIXES
include/GMT
include/GMT
include
PATHS
~/Library/Frameworks/GMT.framework/Headers
/Library/Frameworks/GMT.framework/Headers
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt/gmt
/opt/GMT
/opt
/usr/local
)
find_library (GMT_LIBRARY
NAMES ${_GMT_lib} gmt gmt5.1 gmt5.1.2 gmt5.1.3
HINTS
${GMT_DIR}
${GMT_ROOT}
$ENV{GMT_DIR}
$ENV{GMT_ROOT}
${_GMT_libpath}
PATH_SUFFIXES lib
PATHS
~/Library/Frameworks/GMT.framework
/Library/Frameworks/GMT.framework
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt/gmt
/opt/GMT
/opt
/usr/local
)
include (FindPackageHandleStandardArgs)
find_package_handle_standard_args (GMT DEFAULT_MSG GMT_LIBRARY GMT_INCLUDE_DIR)
set (GMT_LIBRARIES ${GMT_LIBRARY})
set (GMT_INCLUDE_DIRS ${GMT_INCLUDE_DIR})

34
dotests

@ -0,0 +1,34 @@
#!/bin/bash
MAKEMAP="${1:-/tmp/save/build/src/makemap}"
TPATH=tests
SCRW=`tput cols`
set -o pipefail
for n in "$TPATH"/*; do
status=`cat $n|grep "# Status: "|sed "s/# Status: //"`
desc=`cat $n|grep "# Description: "|sed "s/# Description: //"`
hash=`cat $n|grep "# Output hash: "|sed "s/.*: //"`
while [ ${#desc} -lt $((SCRW-5)) ]; do desc+=" "; done
echo -n "$desc"
t=`mktemp`
grep -v -E "# .*: " "$n">$t
thash=`$MAKEMAP "$t" 2>&1 | sha256sum|sed "s/ .*//"`
ret=$?
if [ "$status" == "ok" ]; then
if [ "$hash" == "$thash" -a "$ret" == "0" ]; then
echo -e "\033[32mOk \033[0m"
else
echo -e "\033[31mFail\033[0m"
fi
else
if [ "$hash" == "$thash" -a "$ret" != "0" ]; then
echo -e "\033[32mOk \033[0m"
else
echo -e "\033[31mFail\033[0m"
fi
fi
rm $t
done

127
include/builtin.h

@ -1,20 +1,119 @@
#ifndef BUILTIN_H
#define BUILTIN_H
#include <cmath>
#include "object.h" #include "object.h"
ObjectBase* Arifm_Add(const ObjectList* input); template<class O>
ObjectBase* Arifm_Sub(const ObjectList* input); struct Valued
ObjectBase* Arifm_Mul(const ObjectList* input); {
ObjectBase* Arifm_Div(const ObjectList* input); double operator ()(const O* q, bool* isok) {return q->Value();}
ObjectBase* Arifm_Pow(const ObjectList* input); };
ObjectBase* Arifm_Neg(const ObjectList* input);
ObjectBase* Arifm_Pos(const ObjectList* input); template<>
struct Valued<ObjectString>
{
double operator ()(const ObjectString* q, bool* isok) {double d=0; if(!str2double(q->Value(),&d)) *isok=false; return d;}
};
template<class T> template<class O>
ObjectBase* Get(const ObjectList* input) struct Valuei
{ {
if(input->Size()!=2) return 0; int64_t operator ()(const O* q, bool* isok) {return q->Value();}
const ObjectBase* ob=input->At(0); };
const ObjectBase* name=input->At(1);
if( (!IS_OTYPE(ob,T)) || (!IS_OTYPE(name,ObjectString)) ) return 0; template<>
struct Valuei<ObjectString>
{
int64_t operator ()(const ObjectString* q, bool* isok) {int64_t i=0; if(!str2int(q->Value(),&i)) *isok=false; return i;}
};
template<class T> class OpAdd {public: static T V(T a1, T a2) {return a1+a2;}};
template<class T> class OpSub {public: static T V(T a1, T a2) {return a1-a2;}};
template<class T> class OpMul {public: static T V(T a1, T a2) {return a1*a2;}};
template<class T> class OpDiv {public: static T V(T a1, T a2) {return a1/a2;}};
template<class T> class OpPow {public: static T V(T a1, T a2) {return pow(a1,a2);}};
template<class T> class OpNeg {public: static T V(T a) {return -a;}};
template<class T> class OpPos {public: static T V(T a) {return +a;}};
template<template<typename> class Op, bool intArifm=true>
const ObjectBase* Arifm2(const ObjectList* input)
{
if(input->Size()!=2) return new ObjectError("Arifmetic binary operator","incorrect number of arguments");
const ObjectBase *arg1=input->At(0),*arg2=input->At(1);
OBTypeM<Valuei,ObjectInt,ObjectString> i1(arg1), i2(arg2);
OBTypeM<Valued,ObjectReal,ObjectInt,ObjectString> r1(arg1), r2(arg2);
bool isok1=true, isok2=true;
// Integer arifmetic
if(i1 && i2 && intArifm)
{
int64_t v1=i1(&isok1),v2=i2(&isok2);
if(isok1 && isok2) return new ObjectInt(Op<int64_t>::V(v1,v2));
}
isok1=isok2=true;
// Real arifmetic
if(r1 && r2)
{
double v1=r1(&isok1),v2=r2(&isok2);
if(isok1 && isok2) return new ObjectReal(Op<double>::V(v1,v2));
}
// Analyze error
// Check first argument
switch(r1.Error())
{
case(OBTypeErr::OK): if(!isok1) return new ObjectError("Arifmetic binary operator","failed conversion of first argument to double"); else break;
case(OBTypeErr::NULLPTR): return new ObjectError("Arifmetic binary operator","first argument is nullptr"); // Impossible case
case(OBTypeErr::TYPEMISMATCH): return new ObjectError("Arifmetic binary operator","first argument has incorrect type");
}
// Firs argument is ok, check second
switch(r2.Error())
{
case(OBTypeErr::OK): if(!isok2) return new ObjectError("Arifmetic binary operator","failed conversion of second argument to double"); else break;
case(OBTypeErr::NULLPTR): return new ObjectError("Arifmetic binary operator","second argument is nullptr"); // Impossible case
case(OBTypeErr::TYPEMISMATCH): return new ObjectError("Arifmetic binary operator","second argument has incorrect type");
}
return new ObjectError("Arifmetic binary operator","unknown error"); // Impossible case
}
template<template<typename> class Op, bool intArifm=true>
const ObjectBase* Arifm1(const ObjectList* input)
{
if(input->Size()!=1) return new ObjectError("Arifmetic unary operator","incorrect number of arguments");
const ObjectBase *arg=input->At(0);
OBTypeM<Valuei,ObjectInt,ObjectString> i(arg);
OBTypeM<Valued,ObjectReal,ObjectString> r(arg);
bool isok;
// Integer arifmetic
isok=true;
if(i && intArifm)
{
int64_t v=i(&isok);
if(isok) return new ObjectInt(Op<int64_t>::V(v));
}
// Real arifmetic
isok=true;
if(r)
{
double v=r(&isok);
if(isok) return new ObjectReal(Op<double>::V(v));
}
// Analyze error
switch(r.Error())
{
case(OBTypeErr::OK): if(!isok) return new ObjectError("Arifmetic unary operator","failed conversion of argument to double"); else break;
case(OBTypeErr::NULLPTR): return new ObjectError("Arifmetic unary operator","argument is nullptr"); // Impossible case
case(OBTypeErr::TYPEMISMATCH): return new ObjectError("Arifmetic unary operator","argument has incorrect type");
}
return dynamic_cast<const T*>(ob)->Get(dynamic_cast<const ObjectString*>(name)->Value()); return new ObjectError("Arifmetic unary operator","unknown error"); // Impossible case
} }
#endif

439
include/common.h

@ -1,51 +1,185 @@
#ifndef COMMON_H #ifndef COMMON_H
#define COMMON_H #define COMMON_H
#include <cctype>
#include <cfloat>
#include <deque>
#include <iostream> #include <iostream>
#include <list>
#include <memory> #include <memory>
#include <set> #include <set>
#include <stack>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <tuple>
#include <typeindex> #include <typeindex>
#include <typeinfo> #include <typeinfo>
#include <vector>
#define EXPORT __attribute__ ((visibility ("default"))) #define EXPORT __attribute__ ((visibility ("default")))
enum debug_level {INTERNALREQUEST,MOREDEBUG,DEBUG,INFO,NORMAL,WARNING,ERROR}; enum debug_level {INTERNALREQUEST,MOREDEBUG,DEBUG,INFO,NORMAL,WARNING,ERROR};
EXPORT std::ostream& COUT(debug_level dl); EXPORT std::ostream& COUT(debug_level dl);
// Check if pointer is ObjectBase derivative class
#define IS_OTYPE(quo,equ) (std::type_index(typeid(*quo))==std::type_index(typeid(equ)))
#define IS_OTYPEI(quo,equ) (std::type_index(typeid(quo))==std::type_index(typeid(equ)))
typedef std::set<std::string> UsedType; typedef std::set<std::string> UsedType;
// We use different format for conversion of double to string
template<class T>
inline std::string ToString(T n) { return std::to_string(n); }
template<>
inline std::string ToString<double>(double n)
{
char buffer[32];
int i;
i=snprintf(buffer,32,"%.*G",DBL_DIG,n);
return std::string(buffer,i);
}
template<>
inline std::string ToString<std::string>(std::string s) {return s;}
// Base class for all objects // Base class for all objects
class EXPORT ObjectBase class EXPORT ObjectBase
{ {
protected: protected:
// No save by default bool err;
virtual const int8_t* Blob(size_t* size) const { *size=0; return 0; } // No save by default
virtual void DeallocBlob(const void* ptr) const {}; virtual const int8_t* Blob(size_t* size) const { *size=0; return nullptr; }
public: virtual void DeallocBlob(const int8_t* ptr) const {};
ObjectBase():err(false) {}
public:
ObjectBase() = default;
ObjectBase(const ObjectBase&) = delete; ObjectBase(const ObjectBase&) = delete;
bool Save(const char* fname) const; bool Save(const char* fname) const;
bool isError() const {return err;}
// Pure virtual api // Pure virtual api
virtual ~ObjectBase(){} virtual ~ObjectBase(){}
virtual ObjectBase* Copy() const=0; virtual const ObjectBase* Copy() const=0;
virtual bool Print() const=0; virtual bool Print() const=0;
virtual std::string Type() const=0; virtual std::string Type() const=0;
// Virtual api with default functions. Modules types must not override them. // Virtual api with default functions. Modules types must not override them.
virtual std::string Dump() const {return "%"+Type()+"%";} virtual std::string Dump() const {return "%"+Type()+"%";}
virtual ObjectBase* Evaluate(bool* err) {*err=false; return 0;}
virtual ObjectBase* ReplaceVar(const std::string& vname, ObjectBase* ob) {return 0;}
virtual void UsedFuncs(UsedType& funcs) const {}
virtual void UsedIdents(UsedType& ids) const {}
}; };
enum class OBTypeErr {OK,NULLPTR,TYPEMISMATCH};
// Template for checking and using ObjectBase derivative classes
// Checking if arg is non-zero: if(OBType<Derived>(arg).Exist())
// Checking if arg is pointer on Derived: if(OBType<Derived>(arg))
// Using const ObjectBase* arg as const Derived*: OBType<Derived>(arg)->SomeCall()
template<class O>
class OBType
{
const O* p;
bool iszero;
public:
OBType() = delete;
OBType(OBType&&) = delete;
OBType(OBType&) = delete;
OBType(const ObjectBase* arg):iszero(nullptr==arg) {if(nullptr==arg) p=nullptr; else if(typeid(*arg)==typeid(O)) p=dynamic_cast<const O*>(arg); else p=nullptr;}
const O* operator->() const {return p;}
operator bool() const {return nullptr!=p;}
operator const O*() const {return p;}
bool Exist() const {return !iszero;}
OBTypeErr Error() const {if(iszero) return OBTypeErr::NULLPTR; else if(nullptr==p) return OBTypeErr::TYPEMISMATCH; else return OBTypeErr::OK;}
};
// Template for checking and using several ObjectBase derivative classes
// Func is template of class-function template argument of which can be any of Derived classes.
// It must have at least one argument - pointer to constant object of Derived class and return non-void.
// Checking if arg is non-zero: if(OBTypeM<Func,Derived...>(arg).Exist())
// Checking if arg is pointer on any of Derived classes (always false if arg is zero): if(OBTypeM<Func,Derived...>(arg))
// Applying Func can be done by two ways:
// 1) Function bool Apply(Res& res, args...). Here res is result of calling Func with arguments args. Result of Func is statically casted to type Res.
// If arg is zero or not a pointer on any of Derived classes, Apply() return false and res is not changed.
// 2) Overloaded operator ()(args). It returns result of calling Func with arguments args. Type of returning value is type of Func<Derived1>().
// Full definition. Never instantiated.
template<template<typename> class Func, class... O>
class OBTypeM;
// Recursive partial instantiation
template<template<typename> class Func, class O1, class... O>
class OBTypeM<Func,O1,O...>: public OBTypeM<Func,O...>
{
bool right;
OBTypeM() = delete;
OBTypeM(OBTypeM&&) = delete;
OBTypeM(OBTypeM&) = delete;
protected:
const ObjectBase* P() const {return OBTypeM<Func,O...>::P();}
template<class Res, class... Args>
Res F(Args... args) const
{
if(right) return Func<O1>()(dynamic_cast<const O1*>(OBTypeM<Func,O...>::P()),args...);
else return OBTypeM<Func,O...>::template F<Res,Args...>(args...);
}
public:
OBTypeM(const ObjectBase* arg):OBTypeM<Func,O...>(arg) {if(nullptr==arg) right=false; else right=(typeid(*arg)==typeid(O1));}
operator bool() const {return right || OBTypeM<Func,O...>::operator bool();}
template<class Res, class... Args>
bool Apply(Res& res, Args... args) const
{
if(!right) return OBTypeM<Func,O...>::Apply(res,args...);
res=static_cast<Res>(Func<O1>()(dynamic_cast<const O1*>(OBTypeM<Func,O...>::P()),args...));
return true;
}
template<class... Args>
auto operator ()(Args... args) const -> decltype(Func<O1>()(dynamic_cast<const O1*>(this->P()),args...))
{
typedef decltype(Func<O1>()(dynamic_cast<const O1*>(this->P()),args...)) T;
if(right) return Func<O1>()(dynamic_cast<const O1*>(OBTypeM<Func,O...>::P()),args...);
else return OBTypeM<Func,O...>::template F<T,Args...>(args...);
}
bool Exist() const {return OBTypeM<Func,O...>::Exist();}
OBTypeErr Error() const {if(!Exist()) return OBTypeErr::NULLPTR; else if(!operator bool()) return OBTypeErr::TYPEMISMATCH; else return OBTypeErr::OK;}
};
// Partial instantiation of the bottom of recursion
template<template<typename> class Func>
class OBTypeM<Func>
{
const ObjectBase* p; // We save pointer on bottom
OBTypeM() = delete;
OBTypeM(OBTypeM&&) = delete;
OBTypeM(OBTypeM&) = delete;
protected: // Only protected functions because such objects must never exists in the wild.
OBTypeM(const ObjectBase* arg):p(arg) {}
operator bool() const {return false;} // If we go down to this place, p is not an pointer on appropriate type
const ObjectBase* P() const {return p;}
template<class Res, class... Args>
bool Apply(Res& res, Args... args) const {return false;}
template<class... Args>
void operator ()(Args... args) const {}
template<class Res, class... Args>
Res F(Args... args) const {return Res();}
bool Exist() const {return nullptr!=p;}
};
// Error class
class EXPORT ObjectError: public ObjectBase
{
std::string function, reason;
ObjectError() = delete;
ObjectError(const ObjectError&) = delete;
ObjectError(ObjectError&&) = delete;
public:
template<class F, class R>
ObjectError(F f, R r):function(f),reason(r) {ObjectBase::err=true;}
const std::string& Function() const {return function;}
const std::string& Reason() const {return reason;}
// Pure virtual overrides
const ObjectBase* Copy() const override {return new ObjectError(function,reason);}
bool Print() const override
{
COUT(NORMAL)<<std::endl<<"Object type: "<<Type()<<std::endl;
COUT(NORMAL)<<"Value: "<<Function()<<": "<<Reason()<<std::endl;
return true;
}
std::string Type() const override {return "ERROR";}
};
// Template for objects without specific constructor/destructor // Template for objects without specific constructor/destructor
template<class T> template<class T>
class EXPORT ObjectSimple: public ObjectBase class EXPORT ObjectSimple: public ObjectBase
@ -57,15 +191,16 @@ private:
const int8_t* Blob(size_t* size) const override {*size=sizeof(T); return reinterpret_cast<const int8_t*>(&val);} const int8_t* Blob(size_t* size) const override {*size=sizeof(T); return reinterpret_cast<const int8_t*>(&val);}
public: public:
ObjectSimple() = delete;
ObjectSimple(T t):val(t) {} ObjectSimple(T t):val(t) {}
ObjectSimple(const T* t):val(*t) {} ObjectSimple(const T* t):val(*t) {}
~ObjectSimple() {} ~ObjectSimple() {}
// Pure virtual overrides // Pure virtual overrides
ObjectBase* Copy() const override {return new ObjectSimple<T>(val);} const ObjectBase* Copy() const override {return new ObjectSimple<T>(val);}
bool Print() const override bool Print() const override
{ {
COUT(NORMAL)<<std::endl<<"Object type: "<<Type()<<std::endl; COUT(NORMAL)<<std::endl<<"Object type: "<<Type()<<std::endl;
COUT(NORMAL)<<"Value: "<<val<<std::endl; COUT(NORMAL)<<"Value: "<<ToString(val)<<std::endl;
return true; return true;
} }
std::string Type() const override {return type;} std::string Type() const override {return type;}
@ -77,13 +212,12 @@ public:
if(std::type_index(typeid(T))==std::type_index(typeid(std::string))) if(std::type_index(typeid(T))==std::type_index(typeid(std::string)))
s<<"\""<<val<<"\""; s<<"\""<<val<<"\"";
else else
s<<val; s<<ToString(val);
return s.str(); return s.str();
} }
// Own functions // Own functions
T Value() const {return val;} T Value() const {return val;}
void SetValue(T s) {val=s;}
}; };
// Simple objects // Simple objects
@ -104,23 +238,17 @@ class EXPORT ObjectPair: public ObjectBase
{ {
private: private:
std::string name; std::string name;
std::shared_ptr<ObjectBase> val; std::shared_ptr<const ObjectBase> val;
ObjectPair(const ObjectPair* p):name(p->name),val(p->val) {}
public: public:
ObjectPair() {} ObjectPair() = delete;
ObjectPair(const std::string& n, ObjectBase* v):name(n) {val.reset(v);} ObjectPair(const std::string& n, const ObjectBase* v):name(n),val(v) {}
ObjectPair(const std::string* n, ObjectBase* v):name(*n) {val.reset(v);} ObjectPair(const std::string* n, const ObjectBase* v):name(*n),val(v) {}
~ObjectPair() {}
// Pure virtual overrides // Pure virtual overrides
ObjectBase* Copy() const override const ObjectBase* Copy() const override {return new ObjectPair(this);}
{
auto ret=new ObjectPair;
ret->name=name; ret->val=val;
return ret;
}
bool Print() const override bool Print() const override
{ {
if(!Exist()) return false;
COUT(NORMAL)<<std::endl<<"Object type: "<<Type()<<std::endl; COUT(NORMAL)<<std::endl<<"Object type: "<<Type()<<std::endl;
COUT(NORMAL)<<"Name is: "<<Name()<<std::endl; COUT(NORMAL)<<"Name is: "<<Name()<<std::endl;
COUT(NORMAL)<<"Value type: "<<val->Type()<<std::endl; COUT(NORMAL)<<"Value type: "<<val->Type()<<std::endl;
@ -130,61 +258,106 @@ public:
// Non-default overrides // Non-default overrides
std::string Dump() const override { return Name()+"="+val->Dump(); } std::string Dump() const override { return Name()+"="+val->Dump(); }
ObjectBase* Evaluate(bool* err) override
{
auto p=val->Evaluate(err);
if(*err)
{
COUT(ERROR)<<" in pair "<<Dump()<<std::endl;
return 0;
}
if(0==p) return 0;
val.reset(p);
return 0;
}
ObjectBase* ReplaceVar(const std::string& vname, ObjectBase* ob) override
{
auto p=val->ReplaceVar(vname,ob);
if(0!=p) val.reset(p);
return 0;
}
void UsedFuncs(UsedType& funcs) const override {return val->UsedFuncs(funcs);}
void UsedIdents(UsedType& ids) const override {return val->UsedIdents(ids);}
// Own functions // Own functions
bool Exist() const {return 0!=val.get();} const ObjectBase* Get(const std::string& gname) const
ObjectBase* Get(const std::string& gname) const
{ {
if(gname==name) return val->Copy(); if(gname==name) return val->Copy();
else return 0; else return new ObjectError("ObjectPair Get","pair has name "+name+" not "+gname);
}
// This function is same as Get but return pointer on non-copied object (this pointer must not be deleted)
const ObjectBase* Find(const std::string& gname) const
{
if(gname==name) return val.get();
else return nullptr;
} }
std::string Name() const {return name;} std::string Name() const {return name;}
void SetPair(const std::string& n, ObjectBase* v) {if(!Exist()) {name=n; val.reset(v);}}
const ObjectBase* Value() const {return val.get();} const ObjectBase* Value() const {return val.get();}
}; };
// Class for objects list // Class for objects list
class EXPORT ObjectList: public ObjectBase class EXPORT ObjectList: public ObjectBase
{ {
public: private:
typedef std::vector<ObjectBase*> ListValues; typedef std::deque<const ObjectBase*> ListValues;
private: public:
typedef ListValues::size_type IndexType;
private:
// Forward iterator for ObjectList (may be later be bidirectional)
class ObjectListIterator: public std::iterator<std::forward_iterator_tag, const ObjectBase*>
{
const ObjectList* list;
std::stack<std::tuple<const ObjectList*, IndexType> > st;
IndexType pos;
void Increment()
{
while(true)
{
OBType<ObjectList> islist(list->At(pos));
if(islist && islist->Size()>0)
{
st.emplace(std::make_tuple(list,pos));
list=islist;
pos=0;
}
else return;
}
}
public:
ObjectListIterator():list(nullptr),pos(0) {}
ObjectListIterator(const ObjectListIterator&) = default;
ObjectListIterator(ObjectListIterator&&) = default;
ObjectListIterator(const ObjectList* l):list(l),pos(0) {if(list) Increment();}
bool operator ==(const ObjectListIterator& i) const {return (list==i.list && pos==i.pos);}
bool operator !=(const ObjectListIterator& i) const {return !operator==(i);}
ObjectListIterator& operator++()
{
if(pos<list->Size()-1)
{
pos++;
Increment();
return *this;
}
while(st.size()>0)
{
std::tie(list,pos)=st.top();
st.pop();
if(pos<list->Size()-1)
{
pos++;
Increment();
return *this;
}
}
// This is the end
list=nullptr;
pos=0;
return *this;
}
ObjectListIterator operator++(int) {ObjectListIterator tmp(*this); operator++(); return tmp;}
const ObjectBase* operator*() const {return list->At(pos);}
const ObjectBase* operator->() const {return list->At(pos);}
};
std::shared_ptr<ListValues> vals; std::shared_ptr<ListValues> vals;
ObjectList(const ObjectList* o):vals(o->vals) {}
public: public:
typedef ObjectListIterator iterator;
typedef ObjectListIterator const_iterator;
ObjectList() {vals.reset(new ListValues, [](ListValues* p){for(auto& i: *p) delete i; delete p;});} ObjectList() {vals.reset(new ListValues, [](ListValues* p){for(auto& i: *p) delete i; delete p;});}
ObjectList(ObjectBase* o) {vals.reset(new ListValues, [](ListValues* p){for(auto& i: *p) delete i; delete p;}); PushBack(o);} ObjectList(const ObjectBase* o) {vals.reset(new ListValues, [](ListValues* p){for(auto& i: *p) delete i; delete p;}); PushBack(o);}
~ObjectList() {}
// Pure virtual overrides // Pure virtual overrides
ObjectBase* Copy() const override const ObjectBase* Copy() const override {return new ObjectList(this);}
{
auto ret=new ObjectList;
ret->vals=vals;
return ret;
}
bool Print() const override bool Print() const override
{ {
if(!Exist()) return false;
COUT(NORMAL)<<std::endl<<"Object type: "<<Type()<<std::endl; COUT(NORMAL)<<std::endl<<"Object type: "<<Type()<<std::endl;
COUT(NORMAL)<<"Number of elements: "<<Size()<<std::endl; COUT(NORMAL)<<"Number of elements: "<<Size()<<std::endl;
return true; return true;
@ -199,63 +372,38 @@ public:
if(vals->size()!=0) s.resize(s.length()-2); if(vals->size()!=0) s.resize(s.length()-2);
return s+")"; return s+")";
} }
ObjectBase* Evaluate(bool* err) override
{
ObjectBase* p;
for(auto& i: *vals)
{
p=i->Evaluate(err);
if(*err)
{
COUT(ERROR)<<" in list member "<<i->Dump()<<std::endl;
return 0;
}
if(0!=p)
{
delete i;
i=p;
}
}
return 0;
}
ObjectBase* ReplaceVar(const std::string& vname, ObjectBase* ob) override
{
ObjectBase* p;
for(auto& i: *vals)
{
p=i->ReplaceVar(vname,ob);
if(0!=p)
{
delete i;
i=p;
}
}
return 0;
}
void UsedFuncs(UsedType& funcs) const override {for(auto& i: *vals) i->UsedFuncs(funcs);}
void UsedIdents(UsedType& ids) const override {for(auto& i: *vals) i->UsedIdents(ids);}
// Own functions // Own functions
const ObjectBase* At(ListValues::size_type i) const {return (*vals)[i];} const ObjectBase* At(IndexType i) const {return (*vals)[i];}
bool Exist() const {return 0!=vals->size();} const ObjectBase* Get(const std::string& gname) const
ObjectBase* Get(const std::string& gname) const {
const ObjectBase* p=Find(gname);
return (nullptr==p)?new ObjectError("ObjectList Get","name "+gname+" not found in list"):p->Copy();
}
// This function is same as Get but return pointer on non-copied object (this pointer must not be deleted)
const ObjectBase* Find(const std::string& gname) const
{ {
ObjectBase* p; const ObjectBase* p;
for(auto& i: *vals) for(const auto& i: *vals)
{ {
p=0; p=nullptr;
if(IS_OTYPE(i,ObjectPair)) p=dynamic_cast<const ObjectPair*>(i)->Get(gname); OBType<ObjectPair> pair(i);
if(0!=p) return p; OBType<ObjectList> list(i);
if(IS_OTYPE(i,ObjectList)) p=dynamic_cast<const ObjectList*>(i)->Get(gname); if(pair) p=pair->Find(gname);
if(0!=p) return p; else if(list) p=list->Find(gname);
if(nullptr!=p) return p;
} }
return 0; return nullptr;
} }
ListValues::size_type Size() const {return vals->size();} IndexType Size() const {return vals->size();}
ObjectList* PushBack(ObjectBase* p) {vals->push_back(p); return this;} ObjectList* PushBack(const ObjectBase* p) {vals->push_back(p); return this;}
ObjectList* PushFront(const ObjectBase* p) {vals->push_front(p); return this;}
// Iterator functions
const_iterator begin() const {return const_iterator(this);}
const_iterator end() const {return const_iterator();}
}; };
typedef ObjectBase* (*Func)(const ObjectList*); typedef const ObjectBase* (*Func)(const ObjectList*);
typedef int (*ModuleInitFunc)(const void*); typedef int (*ModuleInitFunc)(const void*);
extern "C" { extern "C" {
@ -263,4 +411,61 @@ EXPORT void RegisterFunction(const std::string& name, Func func);
EXPORT int LoadModule(const std::string& name, const void* p, const std::string& modname=""); EXPORT int LoadModule(const std::string& name, const void* p, const std::string& modname="");
} }
template<class T>
const ObjectBase* Get(const ObjectList* input)
{
if(input->Size()!=2) return new ObjectError("GET","incorrect number of arguments");
OBType<T> ob(input->At(0));
OBType<ObjectString> name(input->At(1));
if(!name) return new ObjectError("GET","second argument is not ObjectString");
if(!ob) return new ObjectError("GET","incorrect type of first argument");
return ob->Get(name->Value());
}
// Simple conversion functions
inline bool str2double(const char* str, double* res)
{
char* pos;
*res=strtod(str,&pos);
if('\0'!=*pos) return false; else return true;
}
inline bool str2int(const char* str, int64_t* res)
{
char* pos;
*res=strtoll(str,&pos,0);
if('\0'!=*pos) return false; else return true;
}
inline bool str2uint(const char* str, uint64_t* res)
{
char* pos;
*res=strtoull(str,&pos,0);
if('\0'!=*pos) return false; else return true;
}
inline bool str2double(const std::string& str, double* res) {return str2double(str.c_str(),res);}
inline bool str2int(const std::string& str, int64_t* res) {return str2int(str.c_str(),res);}
inline bool str2uint(const std::string& str, uint64_t* res) {return str2uint(str.c_str(),res);}
inline void tolower(std::string& str) {for(auto& p: str) p=tolower(p);}
inline void tolower(std::string* str) {for(auto& p:*str) p=tolower(p);}
inline void tolower(char* str) {for(*str=tolower(*str);'\0'!=*str++;*str=tolower(*str));}
typedef std::list<std::string> WordList;
EXPORT WordList Split(const std::string& str, const std::string& delims, bool allowempty);
inline WordList Split(const std::string& str) {return Split(str,std::string(" \t",3),false);}
inline WordList Split(const char* str) {return Split(std::string(str),std::string(" \t",3),false);}
inline WordList Split(const std::string& str, bool allowempty) {return Split(str,std::string(" \t",3),allowempty);}
inline WordList Split(const char* str, bool allowempty) {return Split(std::string(str),std::string(" \t",3),allowempty);}
inline WordList Split(const std::string& str, const std::string& delims) {return Split(str,delims,false);}
inline WordList Split(const char* str, const std::string& delims) {return Split(std::string(str),delims,false);}
inline WordList Split(const std::string& str, const char* delims) {return Split(str,std::string(delims),false);}
inline WordList Split(const char* str, const char* delims) {return Split(std::string(str),std::string(delims),false);}
#endif #endif

18
include/deptree.h

@ -12,7 +12,7 @@ class DepTree
typedef std::vector<DepTree*> LeafVector; typedef std::vector<DepTree*> LeafVector;
typedef std::map<std::string,DepTree*> DepTreeVars; typedef std::map<std::string,DepTree*> DepTreeVars;
typedef std::set<DepTree*> NodeVector; typedef std::set<DepTree*> NodeVector;
typedef std::set<DepTree*> CallStack; typedef std::set<const DepTree*> CallStack;
enum NodeType {NOTDEF,ROOT,SAVE,PRINT,VAR}; enum NodeType {NOTDEF,ROOT,SAVE,PRINT,VAR};
typedef struct typedef struct
@ -22,23 +22,19 @@ class DepTree
LeafVector leafs; LeafVector leafs;
pthread_mutex_t leaf_mtx; pthread_mutex_t leaf_mtx;
pthread_mutex_t root_mtx; pthread_mutex_t root_mtx;
pthread_mutex_t vars_mtx;
pthread_mutex_t prsv_mtx;
pthread_mutex_t tree_mtx; pthread_mutex_t tree_mtx;
} thread_params; } thread_params;
NodeVector parents; NodeVector parents;
NodeVector childrens; NodeVector childrens;
NodeType type; NodeType type;
G_toType::size_type index;
std::string name; std::string name;
mutable bool visited; ExecExpr exe;
int CreateNodeFromVar(const std::string& var, DepTreeVars& vars, CallStack& callstack); int CreateNodeFromVar(const std::string& v, VarType& vars, DepTreeVars& dvars, CallStack& callstack);
int CreateNodeFromSP(NodeType list, G_toType::size_type ind, DepTreeVars& vars); int CreateNodeFromSP(NodeType list, VarType& vars, ExecExpr& exp, DepTreeVars& dvars);
LeafVector FindLeafNodes() const; LeafVector FindLeafNodes() const;
public: public:
DepTree():type(DepTree::NOTDEF),visited(false) {} DepTree():type(DepTree::NOTDEF) {}
DepTree(const DepTree&) = delete; DepTree(const DepTree&) = delete;
~DepTree() ~DepTree()
{ {
@ -50,8 +46,10 @@ public:
} }
} }
int CreateGlobalTree(UsedType& used); int CreateTree(ExecType& save, ExecType& print, VarType& vars);
int EvaluateTree(unsigned int nthreads); int EvaluateTree(unsigned int nthreads);
void DumpTree() const;
int CheckFunctions() const;
friend void* TreeEvaluateM(void* arg); friend void* TreeEvaluateM(void* arg);
friend void* TreeEvaluate (void* arg); friend void* TreeEvaluate (void* arg);
}; };

15
include/globals.h

@ -1,25 +1,18 @@
#ifndef GLOBALS_H #ifndef GLOBALS_H
#define GLOBALS_H #define GLOBALS_H
#include "dlfcn.h" #include <dlfcn.h>
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include "object.h" #include "object.h"
// Variables definitions
typedef std::map<std::string,ObjectBase*> G_varsType;
extern G_varsType G_vars;
// Functions addresses // Functions addresses
typedef std::multimap<std::string,Func> G_funcsType; typedef std::multimap<std::string,Func> G_funcsType;
extern G_funcsType G_funcs; extern G_funcsType G_funcs;
typedef std::vector<ObjectList*> G_toType; // Types for variables and printsave store
// List of objects to save typedef std::map<std::string,ExecExpr> VarType;
extern G_toType G_tosave; typedef std::vector<ExecExpr> ExecType;
// List of objects to print
extern G_toType G_toprint;
// Loaded modules // Loaded modules
typedef std::vector<void*> G_libsType; typedef std::vector<void*> G_libsType;

5
include/init.h

@ -10,10 +10,7 @@ struct program_options
const char* config; const char* config;
}; };
int BuildDepTree(DepTree* deptree, UsedType& used); int ParseConfigFile(const char* config, ExecType& tosave, ExecType& toprint, VarType& vars);
int CheckFunctions();
void DumpConfig();
int ParseConfigFile(const char* config);
int ParseOptions(int argc, char** argv, struct program_options& options); int ParseOptions(int argc, char** argv, struct program_options& options);
int RegisterBuiltinFunctions(); int RegisterBuiltinFunctions();
debug_level SetDebugLevel(debug_level dl=INTERNALREQUEST); debug_level SetDebugLevel(debug_level dl=INTERNALREQUEST);

145
include/object.h

@ -1,84 +1,111 @@
#ifndef OBJECT_H #ifndef OBJECT_H
#define OBJECT_H #define OBJECT_H
#include <stdio.h>
#include <errno.h> #include <errno.h>
#include <string.h>
#include <inttypes.h> #include <inttypes.h>
#include <stack>
#include <stdio.h>
#include <string.h>
#include "common.h" #include "common.h"
// Class for storing identifiers // Bison location
class OId: public ObjectBase struct grammatic_location
{ {
std::string name; struct incloc
{
int line,column;
std::string filename;
};
int first_line;
int first_column;
int last_line;
int last_column;
std::list<struct incloc> incstack;
std::string filename;
};
class StackElem
{
StackElem()=delete;
StackElem(const StackElem&)=delete;
public: public:
OId(const std::string* t):name(*t) {} enum Type {TYPE_EMPTY,TYPE_BEGINLIST,TYPE_ENDLIST,TYPE_MKPAIR,TYPE_OBJECT,TYPE_VARIABLE,TYPE_FUNCTION};
~OId() {} StackElem(Type t, const struct grammatic_location& loc, const char* s=nullptr):type(t),obj(nullptr),location(loc)
// Pure virtual overrides
ObjectBase* Copy() const override
{ {
COUT(WARNING)<<"OId::Copy: this call must never be happens."<<std::endl; if(TYPE_BEGINLIST==t || TYPE_ENDLIST==t) return;
return new OId(&name); if( (TYPE_VARIABLE==t || TYPE_FUNCTION==t || TYPE_MKPAIR==t) && nullptr!=s) name=s;
else type=TYPE_EMPTY;
} }
bool Print() const override {return false;} StackElem(Type t, const struct grammatic_location& loc, const std::string& s):type(t),obj(nullptr),location(loc)
std::string Type() const override {return "IDENT";}
// Non-default overrides
std::string Dump() const override {return name;};
ObjectBase* Evaluate(bool* err) override
{ {
COUT(ERROR)<<"Variable "<<name<<" still not defined."<<std::endl; if(TYPE_VARIABLE==t || TYPE_FUNCTION==t || TYPE_MKPAIR==t) name=s;
*err=true; else type=TYPE_EMPTY;
return 0;
} }
ObjectBase* ReplaceVar(const std::string& vname, ObjectBase* ob) override StackElem(ObjectBase* o, const struct grammatic_location& loc):type(TYPE_OBJECT),obj(o),location(loc) {}
~StackElem() {if(TYPE_OBJECT==type) delete obj;}
StackElem(StackElem&& s):type(s.type),name(s.name),location(s.location)
{ {
if(vname==Name()) return ob->Copy(); if(TYPE_OBJECT==type)
else return 0; {
// Simple copy of pointer
obj=s.obj;
// Prevent destruction of ObjectBase in StackElem destructor
s.type=TYPE_EMPTY;
}
} }
void UsedIdents(UsedType& ids) const override {ids.insert(name);}
// Own functions
std::string Name() const {return name;}
// void SetName(const std::string& s) {name=s;}
};
// Class for storing functions const ObjectBase* Object() const {return isObject()?obj:nullptr;}
class OFunc: public ObjectBase // Detach object
{ const ObjectBase* PickObject()
std::string name;
ObjectList* args;
public:
OFunc(const std::string* t, ObjectBase* p):name(*t)
{ {
if(IS_OTYPE(p,ObjectList)) args=dynamic_cast<ObjectList*>(p); if(isObject())
else args=new ObjectList(p);
}
OFunc(const char* t, ObjectBase* p):name(t)
{ {
if(IS_OTYPE(p,ObjectList)) args=dynamic_cast<ObjectList*>(p); type=TYPE_EMPTY;
else args=new ObjectList(p); return obj;
}
else return nullptr;
} }
~OFunc() {if(args!=0) delete args;} bool ReplaceByObject(const ObjectBase* ob)
// Pure virtual overrides
ObjectBase* Copy() const override
{ {
COUT(WARNING)<<"OFunc::Copy: this call must never be happens."<<std::endl; if(isObject()) {delete ob; return false;}
return new OFunc(&name,args->Copy()); type=TYPE_OBJECT;
obj=ob;
return true;
} }
bool Print() const override {return false;} std::string Name() const {return (isVar() || isFunc() || isMKPair())?name:std::string();}
std::string Type() const override {return "FUNC";} bool isObject() const {return (TYPE_OBJECT==type);}
bool isVar() const {return (TYPE_VARIABLE==type);}
bool isFunc() const {return (TYPE_FUNCTION==type);}
bool isBList() const {return (TYPE_BEGINLIST==type);}
bool isEList() const {return (TYPE_ENDLIST==type);}
bool isMKPair() const {return (TYPE_MKPAIR==type);}
const struct grammatic_location& Location() const {return location;}
Type T() const {return type;}
// Non-default overrides private:
std::string Dump() const override {return Name()+args->Dump();}; Type type;
ObjectBase* Evaluate(bool* err) override; const ObjectBase* obj;
ObjectBase* ReplaceVar(const std::string& vname, ObjectBase* ob) override {return args->ReplaceVar(vname,ob);} std::string name;
void UsedFuncs(UsedType& funcs) const override {funcs.insert(name); args->UsedFuncs(funcs);} struct grammatic_location location;
void UsedIdents(UsedType& ids) const override {args->UsedIdents(ids);}
// Own functions
std::string Name() const {return name;}
// void SetName(std::string s) {name=s;}
}; };
typedef std::list<StackElem> ExecExpr;
inline StackElem SEBList(const struct grammatic_location& loc) {return StackElem(StackElem::TYPE_BEGINLIST,loc);}
inline StackElem SEEList(const struct grammatic_location& loc) {return StackElem(StackElem::TYPE_ENDLIST,loc);}
inline StackElem SEObj(ObjectBase* o, const struct grammatic_location& loc) {return StackElem(o,loc);}
inline StackElem SEMKPair(const std::string& s, const struct grammatic_location& loc) {return StackElem(StackElem::TYPE_MKPAIR,loc,s);}
inline StackElem SEVar(const std::string& s, const struct grammatic_location& loc) {return StackElem(StackElem::TYPE_VARIABLE,loc,s);}
inline StackElem SEFunc(const std::string& s, const struct grammatic_location& loc) {return StackElem(StackElem::TYPE_FUNCTION,loc,s);}
inline StackElem SEMKPair(const std::string* s, const struct grammatic_location& loc) {return StackElem(StackElem::TYPE_MKPAIR,loc,*s);}
inline StackElem SEVar(const std::string* s, const struct grammatic_location& loc) {return StackElem(StackElem::TYPE_VARIABLE,loc,*s);}
inline StackElem SEFunc(const std::string* s, const struct grammatic_location& loc) {return StackElem(StackElem::TYPE_FUNCTION,loc,*s);}
std::string DumpExprE(const ExecExpr& exp);
std::string DumpExpr(const ExecExpr& exp);
UsedType UsedVars(const ExecExpr& exp);
UsedType UsedFuncs(const ExecExpr& exp);
const ObjectBase* Evaluate(ExecExpr& exp, bool* err);
//inline void ReplaceVar(ExecExpr& exp, const std::string& var, ObjectBase* ob) {for(auto& se: exp) if(se.isVar() && var==se.Name()) se.ReplaceByObject(ob);}
void ReplaceVar(ExecExpr& exp, const std::string& var, const ObjectBase* ob);// {for(auto& se: exp) if(se.isVar() && var==se.Name()) se.ReplaceByObject(ob);}
#endif #endif

14
include/parser.h

@ -1,8 +1,10 @@
#ifndef PARSER_PARSER_H #ifndef PARSER_PARSER_H
#define PARSER_PARSER_H #define PARSER_PARSER_H
#include <list>
#include <stack> #include <stack>
#include <string> #include <string>
#include <stdio.h> #include <stdio.h>
#include "globals.h"
// State of lexical parser (filename and position in file) // State of lexical parser (filename and position in file)
struct lexical_state struct lexical_state
@ -19,6 +21,8 @@ struct lexical_extra
struct lexical_state state; struct lexical_state state;
unsigned int maxinclevel; unsigned int maxinclevel;
int retcode; int retcode;
VarType *vars;
ExecType *tosave, *toprint;
std::stack<FILE*> fds; std::stack<FILE*> fds;
std::stack<struct lexical_state> states; std::stack<struct lexical_state> states;
~lexical_extra() ~lexical_extra()
@ -34,16 +38,6 @@ struct lexical_extra
} }
}; };
// Bison location
struct grammatic_location
{
int first_line;
int first_column;
int last_line;
int last_column;
std::string filename;
};
#if !defined CONFLTYPE && !defined CONFLTYPE_IS_DECLARED #if !defined CONFLTYPE && !defined CONFLTYPE_IS_DECLARED
typedef struct grammatic_location CONFLTYPE; typedef struct grammatic_location CONFLTYPE;
#define CONFLTYPE_IS_DECLARED 1 #define CONFLTYPE_IS_DECLARED 1

34
maketest

@ -0,0 +1,34 @@
#!/bin/bash
name="$1"
desc="$2"
TLOC="${3:-/tmp/save/test}"
MAKEMAP="${4:-/tmp/save/build/src/makemap}"
if [ -f tests/"$name" ]; then
echo "Test $name already exist."
exit 1
fi
if [ ! -f "$TLOC" ]; then
echo "Configuration file $TLOC not found."
exit 2
fi
if [ ! -x "$MAKEMAP" ]; then
echo "Can't exec file $MAKEMAP."
exit 3
fi
echo "# Description: $desc" >tests/$name
if "$MAKEMAP" "$TLOC" &>/dev/null; then
hash=`"$MAKEMAP" "$TLOC" 2>/dev/null|sha256sum|sed "s/ .*//"`
status=ok
else
hash=`"$MAKEMAP" "$TLOC" 2>&1|sha256sum|sed "s/ .*//"`
status=fail
fi
echo "# Status: $status" >>tests/$name
echo "# Output hash: $hash" >>tests/$name
cat "$TLOC" >>tests/$name

2
modules/gmt/.gitignore vendored

@ -0,0 +1,2 @@
/gmt_filter_headfoot.cpp
/gmt_filter_headfoot.h

3
modules/gmt/CMakeLists.txt

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 2.8.5)
include(BuildModule)

24
modules/gmt/ModuleSetup.cmake

@ -0,0 +1,24 @@
find_package(FLEX REQUIRED)
FLEX_TARGET(GMT_hfScanner ${moddir}/modgmt_filter_headfoot.l ${CMAKE_CURRENT_BINARY_DIR}/modgmt_filter_headfoot.cpp COMPILE_FLAGS "--header-file=${CMAKE_CURRENT_BINARY_DIR}/modgmt_filter_headfoot.h")
set_property(DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES modgmt_filter_headfoot.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
find_package(GMT REQUIRED)
# Search ghostscript
if(NOT GHOSTSCRIPT_ROOT_DIR)
set(GHOSTSCRIPT_ROOT_DIR ${CMAKE_INSTALL_PREFIX} CACHE PATH "Ghostscript root path")
endif()
mark_as_advanced(CLEAR GHOSTSCRIPT_ROOT_DIR)
find_path(GHOSTSCRIPT_INCLUDES NAMES ghostscript/gdevdsp.h ghostscript/iapi.h HINTS ${GHOSTSCRIPT_ROOT_DIR} ${CMAKE_INSTALL_PREFIX})
find_library(GHOSTSCRIPT_LIBRARY gs HINTS ${GHOSTSCRIPT_ROOT_DIR} ${CMAKE_INSTALL_PREFIX})
if(NOT GHOSTSCRIPT_INCLUDES)
message(FATAL_ERROR "Ghostscript includes not found!")
endif()
if(NOT GHOSTSCRIPT_LIBRARY)
message(FATAL_ERROR "Ghostscript library not found!")
endif()
set(MODULE_ADDITIONAL_SOURCES ${FLEX_GMT_hfScanner_OUTPUTS})
set(MODULE_ADDITIONAL_INCLUDES ${GMT_INCLUDE_DIR} ${GHOSTSCRIPT_INCLUDES})
set(MODULE_ADDITIONAL_LIBRARIES ${GMT_LIBRARIES} ${GHOSTSCRIPT_LIBRARY})

35
modules/gmt/makemod

@ -0,0 +1,35 @@
MODLIBS+=-lgmt
GMT_FLSOURCE=modgmt_filter_headfoot.cpp
GMT_FLHEADERS=$(subst .cpp,.h,$(GMT_FLSOURCE))
ifeq ($(subst ,$(GMT_FLSOURCE),$(SOURCE)),$(SOURCE))
SOURCE+=$(GMT_FLSOURCE)
MODSOURCE+=modules/gmt/$(GMT_FLSOURCE)
MODHEADERS+=modules/gmt/$(GMT_FLHEADERS)
endif
ifdef MODULE
modgmt_filters.d: modgmt_filter_headfoot.h
modgmt_filter_headfoot.cpp: modgmt_filter_headfoot.l
flex -o $@ --header-file=modgmt_filter_headfoot.h $<
[ -f modgmt_filter_headfoot.h ] && touch modgmt_filter_headfoot.h
modgmt_filter_headfoot.h: modgmt_filter_headfoot.l modgmt_filter_headfoot.cpp
.PHONY: gmt_clean
clean: gmt_clean
gmt_clean:
rm -f $(GMT_FLSOURCE) $(GMT_FLHEADERS)
else
modules/gmt/modgmt_filter_headfoot.cpp: modules/gmt/modgmt_filter_headfoot.l
make -C modules/gmt -f ../../Makefile MODULE=gmt modgmt_filter_headfoot.cpp
modules/gmt/modgmt_filter_headfoot.h: modules/gmt/modgmt_filter_headfoot.cpp
endif

58
modules/gmt/modgmt.cpp

@ -0,0 +1,58 @@
#include "modgmt.h"
#include "modgmt_func.h"
#include "modgmt_internals.h"
#include "modgmt_gsfuncs.h"
#include "modgmt_map.h"
// Initialisation function
int gmt_module_init(void* p)
{
// Fill header and footer
void* gmtapi;
int ret=0;
gmtapi=GMT_Create_Session("gmt_makemap",2,GMTMODE,0);
if(0==gmtapi) return 1;
if(0==ret) ret=callgmtmodule(gmtapi,"psclip","-C -P -K",&header,gmt_filter_headfoot);
if(0==ret) ret=callgmtmodule(gmtapi,"psclip","-C -P -O",&footer,gmt_filter_headfoot);
GMT_Destroy_Session(gmtapi);
if(0!=ret) return ret;
gmt_projection::FillProjNames();
if(!gmt_font::FillFontNames()) return 1;
RegisterFunction("GMT_Header",GMT_Header);
RegisterFunction("GMT_Footer",GMT_Footer);
RegisterFunction("GET",Get<ObjectGMTCoord>);
RegisterFunction("GET",Get<ObjectGMTRegion>);
RegisterFunction("GET",Get<ObjectGMTProjection>);
RegisterFunction("GET",Get<ObjectGMTColor>);
RegisterFunction("GET",Get<ObjectGMTDash>);
RegisterFunction("GET",Get<ObjectGMTPen>);
RegisterFunction("GET",Get<ObjectGMTFont>);
RegisterFunction("GET",Get<ObjectGMTLayer>);
RegisterFunction("Coord",GMT_Type<struct gmt_coord>);
RegisterFunction("Region",GMT_Type<struct gmt_region>);
RegisterFunction("Projection",GMT_Type<struct gmt_projection>);
RegisterFunction("Color",GMT_Type<struct gmt_color>);
RegisterFunction("ColorGray",GMT_ColorGray);
RegisterFunction("ColorRGB",GMT_ColorRGB);
RegisterFunction("ColorHSV",GMT_ColorHSV);
RegisterFunction("ColorCMYK",GMT_ColorCMYK);
RegisterFunction("Pen",GMT_Type<struct gmt_pen>);
RegisterFunction("Font",GMT_Type<struct gmt_font>);
RegisterFunction("Shift",GMT_LayerShift);
RegisterFunction("DrawFrame",GMT_DrawFrame);
RegisterFunction("Map",GMT_Map);
RegisterFunction("Convert2PDF",GMT_Convert2PDF);
RegisterFunction("Convert2PNG",GMT_Convert2PNG);
RegisterFunction("Convert2JPG",GMT_Convert2JPG);
RegisterFunction("Convert2JPEG",GMT_Convert2JPG);
CheckGhostscriptAbilities();
// Calculating bounding box is critical
if(!gs_abilities.havebbox) return 1;
return 0;
}

9
modules/gmt/modgmt.h

@ -0,0 +1,9 @@
#ifndef MODGMT_H
#define MODGMT_H
#include "common.h"
extern "C" {
EXPORT int gmt_module_init(void* p);
}
#endif

1277
modules/gmt/modgmt_colornames.cpp

File diff suppressed because it is too large Load Diff

84
modules/gmt/modgmt_colornames.h

@ -0,0 +1,84 @@
#ifndef MODGMT_COLORNAMES_H
#define MODGMT_COLORNAMES_H
struct colorname {const char *name; double r; double g; double b; };
#include <string.h>
class ColorHash
{
private:
static inline unsigned int colornamehash (const char *str, unsigned int len);
public:
static const struct colorname *in_colors_set (const char *str, unsigned int len);
};
inline unsigned int
ColorHash::colornamehash (const char *str, unsigned int len)
{
static const unsigned short asso_values[] =
{
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 666, 27,
26, 23, 22, 789, 588, 587, 501, 436, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 61, 96, 222, 98, 22,
117, 22, 561, 559, 22, 708, 179, 301, 42, 101,
242, 46, 23, 22, 116, 171, 821, 809, 2728, 169,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 61, 96, 222,
98, 22, 117, 22, 561, 559, 22, 708, 179, 301,
42, 101, 242, 46, 23, 22, 116, 171, 821, 809,
2728, 169, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728
};
int hval = len;
switch (hval)
{
default:
hval += asso_values[(unsigned char)str[12]];
/*FALLTHROUGH*/
case 12:
hval += asso_values[(unsigned char)str[11]];
/*FALLTHROUGH*/
case 11:
case 10:
case 9:
case 8:
hval += asso_values[(unsigned char)str[7]];
/*FALLTHROUGH*/
case 7:
hval += asso_values[(unsigned char)str[6]];
/*FALLTHROUGH*/
case 6:
hval += asso_values[(unsigned char)str[5]];
/*FALLTHROUGH*/
case 5:
hval += asso_values[(unsigned char)str[4]];
/*FALLTHROUGH*/
case 4:
case 3:
hval += asso_values[(unsigned char)str[2]];
/*FALLTHROUGH*/
case 2:
case 1:
hval += asso_values[(unsigned char)str[0]];
break;
}
return hval + asso_values[(unsigned char)str[len - 1]];
}
#endif

135
modules/gmt/modgmt_colortransform.h

@ -0,0 +1,135 @@
// From grayscale
struct gmt_color Gray2RGB() const
{
struct gmt_color color;
color.transparency=transparency;
color.model=RGB;
color.r=color.g=color.b=gray;
return color;
}
struct gmt_color Gray2HSV() const
{
struct gmt_color color;
color.transparency=transparency;
color.model=HSV;
color.hue=0;
color.saturation=0;
color.value=gray/255.0;
return color;
}
struct gmt_color Gray2CMYK() const
{
struct gmt_color color;
color.transparency=transparency;
color.model=CMYK;
color.cyan=color.magenta=color.yellow=0;
color.black=(1.0-gray/255.0)*100.0;
return color;
}
// From RGB
struct gmt_color RGB2Gray() const
{
struct gmt_color color;
color.transparency=transparency;
color.model=GRAY;
color.gray=0.2126*r+0.7152*g+0.0722*b;
return color;
}
struct gmt_color RGB2HSV() const
{
struct gmt_color color;
color.transparency=transparency;
color.model=HSV;
double rr=r/255.0,gg=g/255.0,bb=b/255.0;
double cmax=std::max(rr,std::max(gg,bb));
double cmin=std::min(rr,std::min(gg,bb));
double delta=cmax-cmin;
if(0==delta) color.hue=0;
else if(cmax==rr)
{
double x=(gg-bb)/delta;
color.hue=x-floor(x/6.0)*6.0;
}
else if(cmax==gg) color.hue=(bb-rr)/delta+2.0;
else if(cmax==bb) color.hue=(rr-gg)/delta+4.0;
color.hue*=60.0;
color.saturation=(0==cmax)?0:(delta/cmax);
color.value=cmax;
return color;
}
struct gmt_color RGB2CMYK() const
{
struct gmt_color color;
color.transparency=transparency;
color.model=CMYK;
double rr=r/255.0,gg=g/255.0,bb=b/255.0;
double k=1.0-std::max(rr,std::max(gg,bb));
color.black=k;
color.cyan=(1.0-rr-k)/(1.0-k);
color.magenta=(1.0-gg-k)/(1.0-k);
color.yellow=(1.0-bb-k)/(1.0-k);
if(0.0==1.0-k) color.cyan=color.magenta=color.yellow=0.0;
color.black*=100.0;
color.cyan*=100.0;
color.magenta*=100.0;
color.yellow*=100.0;
return color;
}
// From HSV
struct gmt_color HSV2Gray() const
{
return HSV2RGB().RGB2Gray();
}
struct gmt_color HSV2RGB() const
{
struct gmt_color color;
color.transparency=transparency;
color.model=RGB;
double c=saturation*value;
double x=c*(1.0-fabs((hue/60.0)-floor((hue/60.0)/2.0)*2.0-1.0));
double m=value-c;
if(0.0 <=hue && 60.0 >hue) {color.r=c; color.g=x; color.b=0;}
if(60.0 <=hue && 120.0>hue) {color.r=x; color.g=c; color.b=0;}
if(120.0<=hue && 180.0>hue) {color.r=0; color.g=c; color.b=x;}
if(180.0<=hue && 240.0>hue) {color.r=0; color.g=x; color.b=c;}
if(240.0<=hue && 300.0>hue) {color.r=x; color.g=0; color.b=c;}
if(300.0<=hue &&360.0>=hue) {color.r=c; color.g=0; color.b=x;}
color.r+=m; color.g+=m; color.b+=m;
color.r*=255; color.g*=255; color.b*=255;
return color;
}
struct gmt_color HSV2CMYK() const
{
return HSV2RGB().RGB2CMYK();
}
// From CMYK
struct gmt_color CMYK2Gray() const
{
return CMYK2RGB().RGB2Gray();
}
struct gmt_color CMYK2RGB() const
{
struct gmt_color color;
color.transparency=transparency;
color.model=RGB;
double cc=cyan/100.0,mm=magenta/100.0,yy=yellow/100.0,kk=black/100.0;
color.r=255.0*(1.0-cc)*(1.0-kk);
color.g=255.0*(1.0-mm)*(1.0-kk);
color.b=255.0*(1.0-yy)*(1.0-kk);
return color;
}
struct gmt_color CMYK2HSV() const
{
return CMYK2RGB().RGB2HSV();
}

46
modules/gmt/modgmt_filter_headfoot.l

@ -0,0 +1,46 @@
%option 8bit reentrant
%option warn
%option yylineno
%option noyywrap
%option prefix="gmt_filter_headfoot"
%option extra-type="std::string*"
%option nounput
%x NOTCOMMENTS
%x SETUP
%x ENDHEAD
%x ENDFOOTER
%{
#include <string>
// flex use register keyword, but it deprecated in C++11.
#define register
// Get rid of warning on unused function
#define YY_NO_INPUT
// struct yyguts_t defined in each flex output file, this emits warning at linking stage
#define yyguts_t gmt_filter_headfootguts_t
#define yyterminate {return 0;}
%}
/*
For header:
1. Remove all comments from begin to %%EndComments inclusively
2. Remove %%Page, %%BeginPageSetup, %%EndPageSetup DSC comments
3. Remove all content between %%BeginSetup and %%EndSetup (skip setpagedevice)
4. Skip code after %%EndPageSetup (draw code)
For footer:
1. Remove all before %%PageTrailer, but not %%PageTrailer itself
*/
%%
<INITIAL>%%EndComments\n BEGIN(NOTCOMMENTS);
<INITIAL>%%PageTrailer\n yyextra->append(yytext,yyleng); BEGIN(ENDFOOTER);
<NOTCOMMENTS>%%Page:.*\n
<NOTCOMMENTS>%%BeginPageSetup.*\n
<NOTCOMMENTS>%%EndPageSetup.*\n BEGIN(ENDHEAD);
<NOTCOMMENTS>%%BeginSetup.*\n BEGIN(SETUP);
<NOTCOMMENTS,ENDFOOTER>.*\n yyextra->append(yytext,yyleng);
<SETUP>%%EndSetup.*\n BEGIN(NOTCOMMENTS);
<INITIAL,SETUP,ENDHEAD>.*\n
<*><<EOF>> yyterminate;
%%

31
modules/gmt/modgmt_filters.cpp

@ -0,0 +1,31 @@
#include "modgmt_filters.h"
#include "modgmt_filter_headfoot.h"
int gmt_filter_default(int fd, std::string* res, void* p)
{
ssize_t br;
char buffer[4096];
do
{
br=read(fd,buffer,4096);
res->append(buffer,br);
} while(0!=br);
close(fd);
return 0;
}
int gmt_filter_headfoot(int fd, std::string* res, void* p)
{
yyscan_t scanner;
FILE* in;
in=fdopen(fd,"r");
gmt_filter_headfootlex_init_extra(res,&scanner);
gmt_filter_headfootset_in(in,scanner);
gmt_filter_headfootlex(scanner);
gmt_filter_headfootlex_destroy(scanner);
fclose(in);
return 0;
}

11
modules/gmt/modgmt_filters.h

@ -0,0 +1,11 @@
#ifndef MODGMT_FILTERS_H
#define MODGMT_FILTERS_H
#include <unistd.h>
#include <string>
typedef int (*gmt_filter)(int, std::string*, void*);
int gmt_filter_default(int, std::string*, void*);
int gmt_filter_headfoot(int, std::string*, void*);
#endif

308
modules/gmt/modgmt_func.cpp

@ -0,0 +1,308 @@
#include "modgmt_func.h"
static double AutoStep(double b, double e)
{
double n=pow(10.0,ceil(log10(e-b)-1.0));
double steps[]={0.5,1,2,5,10,0};
double div=0.0;
uint i=0;
do if((e-b)*steps[i]/n<10.0) div=n/steps[i]; while(0.0!=steps[++i]);
return div;
}
const ObjectBase* GMT_Header(const ObjectList* input)
{
return new ObjectString(header);
}
const ObjectBase* GMT_Footer(const ObjectList* input)
{
return new ObjectString(footer);
}
const ObjectBase* GMT_ColorGray(const ObjectList* input)
{
struct gmt_color c;
RPosPar<Base2RGB> g("gray");
OPosPar<Base2TransD> t("transparency",0.0);
ParsePositionalParameters params(input,g,t);
if(!params) return new ObjectError("ColorGray",params.Error());
c.model=gmt_color::GRAY;
c.gray=g; c.transparency=t;
return new ObjectGMTColor(c);
}
const ObjectBase* GMT_ColorRGB(const ObjectList* input)
{
struct gmt_color c;
RPosPar<Base2RGB> r("red"),g("green"),b("blue");
OPosPar<Base2TransD> t("transparency",0.0);
ParsePositionalParameters params(input,r,g,b,t);
if(!params) return new ObjectError("ColorRGB",params.Error());
c.model=gmt_color::RGB;
c.r=r; c.g=g; c.b=b; c.transparency=t;
return new ObjectGMTColor(c);
}
const ObjectBase* GMT_ColorHSV(const ObjectList* input)
{
struct gmt_color c;
RPosPar<Base2Hue> h("hue");
RPosPar<Base2SV> s("saturation"), v("value");
OPosPar<Base2TransD> t("transparency",0.0);
ParsePositionalParameters params(input,h,s,v,t);
if(!params) return new ObjectError("ColorHSV",params.Error());
c.model=gmt_color::HSV;
c.hue=h; c.saturation=s; c.value=v; c.transparency=t;
return new ObjectGMTColor(c);
}
const ObjectBase* GMT_ColorCMYK(const ObjectList* input)
{
struct gmt_color C;
RPosPar<Base2CMYK> c("cyan"),m("magenta"),y("yellow"),k("black");
OPosPar<Base2TransD> t("transparency",0.0);
ParsePositionalParameters params(input,c,m,y,k,t);
if(!params) return new ObjectError("ColorCMYK",params.Error());
C.model=gmt_color::CMYK;
C.cyan=c; C.magenta=m; C.yellow=y; C.black=k; C.transparency=t;
return new ObjectGMTColor(C);
}
// Shifting layer
/*
Input:
1) Three arguments, first is Layer, second and third are double. Interprets as new absolute position in cm.
2) Pairs list. Names are l (layer), x, y, relx (rx), rely (ry). Pair with name l may be absent, in this case search in list and using as layer object with ObjectGMTLayer type. x and y are absolute positions in cm, relx and rely are shift from current position. x (y) and relx (rely) are mutually exlusive, but x (y) and rely (relx) can be used simultaneously. If position for some axis is absent, then this position is unchanged.
*/
const ObjectBase* GMT_LayerShift(const ObjectList* input)
{
struct gmt_layer layer;
std::string err;
// Case 1
{
RPosPar<Base2Layer> l("layer");
OPosPar<Base2DoubleD> x("x",0.0), y("y",0.0);
{ParsePositionalParameters params(input,l,x,y); if(!params) goto case2;} // Try case 2, if case 1 failed.
layer=l; layer.shiftx=x; layer.shifty=y;
return new ObjectGMTLayer(layer);
}
case2:
{
RNPar<Base2Layer> l("layer");
ONPar<Base2Double> x("x"),y("y"),rx("r[el]x"),ry("r[el]y");
{ParseNamedParameters params(input,l,x,y); if(!params) {err=params.Error(); goto fail;}} // Parsing error
// Check duplicate parameters
if(x.Exist() && rx.Exist()) {err="Only one of "+x.Name()+" or "+rx.Name()+" can be specified."; goto fail;}
if(y.Exist() && ry.Exist()) {err="Only one of "+y.Name()+" or "+ry.Name()+" can be specified."; goto fail;}
// Do shift
if( x.Exist()) layer.shiftx=x;
if( y.Exist()) layer.shifty=y;
if(rx.Exist()) layer.shiftx+=rx;
if(ry.Exist()) layer.shifty+=ry;
}
return new ObjectGMTLayer(layer);
fail:
return new ObjectError("LayerShift",err);
}
// Draw frame with tics
/*
Input: pairs list.
proj or projection or unnamed GMTProjection parameter - projection. This is mandatory argument.
xtics - position of tics for x axis. Can be none, up, down or both. Default is down.
ytics - position of tics for y axis. Can be none, left, right or both. Default is left.
xint - tics interval for x axis. Numerical value or word auto. Default is auto.
yint - tics interval for y axis. Numerical value or word auto. Default is auto.
domain - coordinates domain. Can be pos (0:360), neg (-360:0) or center (-180:180) (only for geographic projections). Default is pos.
mark - using letters (W, E, S, N) instead of sign for coordinates (only for geographic projections). Can be yes or no. Default is no.
font - font using for annotations. Default is 12,Times-Roman,black.
offset - distance from end of tick-mark to start of annotation in cm. Default is 0.18.
framewidth - width of frame (only for geographic projections) in cm. Default is 0.1.
framepen - pen for drawing frame. Default is 0.35,black.
ticklen - lenght of tick-marks in cm. Default is 0.18.
tickpen - pen for drawing ticks. Default is 0.3,black.
trans or transparency - transparency level, double from 0 to 100. Default is 0 (opaque).
model - transparency model, string. Choose from Color, ColorBurn, ColorDodge, Darken, Difference, Exclusion, HardLight, Hue, Lighten, Luminosity, Multiply, Normal, Overlay, Saturation, SoftLight, and Screen. Default is Normal.
rx or relx - shift layer on x cm in horisontal direction. Default is 0.
ry or rely - shift layer on y cm in vertical direction. Default is 0.
*/
template<class O>
using SConvertor=Convert2Struct<std::string, O>;
template<class O>
using DConvertor=Convert2Struct<double, O>;
const ObjectBase* GMT_DrawFrame(const ObjectList* input)
{
struct XYIntVal // Representing intervals for tics intervals
{
bool isauto; double val;
XYIntVal():isauto(true),val(0.0) {} // Default is auto
};
class Base2TInt // Custom convertor class for xy tics intervals
{
public:
using ValueType=struct XYIntVal;
ValueType Convert(const ObjectBase* ob, bool* res, std::string& err)
{
ValueType ret;
ret.isauto=false;
OBTypeM<SConvertor,ObjectString> sp(ob);
OBTypeM<DConvertor,ObjectReal,ObjectInt,ObjectString> dp(ob);
// Check string "auto"
if(sp.Error()==OBTypeErr::OK)
{
std::string s=sp(res,std::ref(err));
if(!res) {err="Unknown error"; goto fail;} // Impossible case
tolower(s);
if("auto"==s) {ret.isauto=true; return ret;}
}
// Check double value
switch(dp.Error())
{
case(OBTypeErr::OK):
{
ret.val=dp(res,std::ref(err));
if(ret.val<=0.0) {err="Tics interval must be greater the zero"; break;}
return ret;
};
case(OBTypeErr::NULLPTR): {err="Can't convert zero ObjectBase pointer to something meaningfull"; break;}
case(OBTypeErr::TYPEMISMATCH): {err="Can't convert "+ob->Type()+" to double: type mismatch"; break;}
}
fail:
*res=false;
return ret;
}
};
std::string err,fakerr;
std::string opts="-P -O -K ";
struct gmt_layer layer;
struct gmt_font deffont;
struct gmt_pen defframepen,deftickpen;
deffont.Convert("12,Times-Roman,black",fakerr);
defframepen.Convert("0.35,black",fakerr);
deftickpen.Convert("0.3,black",fakerr);
RNFPar<Base2Proj,ObjectGMTProjection> proj("projection");
ONPar<Base2TInt> xint("x[ ]interval"), yint("y[ ]interval");
ONPar<Base2StringD> xtics("x[ ]tics[ ]position","down"), ytics("y[ ]t[ics[ ]position","left");
ONPar<Base2StringD> domain("domain","pos");
ONPar<Base2BoolD> mark("mark",false);
ONPar<Base2FontD> font("font",deffont);
ONPar<Base2DoubleD> offset("offset",0.18);
ONPar<Base2NonNegD> framewidth("f[rame][ ]width",0.1);
ONPar<Base2PenD> framepen("f[rame][ ]pen",defframepen), tickpen("t[icks][ ]pen",deftickpen);
ONPar<Base2DoubleD> ticklen("t[icks][ ]length",0.18);
ONPar<Base2TransD> transp("transparency",0.0);
ONPar<Base2StringD> transpmodel("model","Normal");
ONPar<Base2DoubleD> rx("r[el]x",0.0),ry("r[el]y",0.0);
ParseNamedParameters params(input,proj,xint,yint,xtics,ytics,domain,mark,font,offset,framewidth,framepen,tickpen,ticklen,transp,transpmodel,rx,ry); if(!params) {err=params.Error(); goto fail;} // Error parsing parameters
layer.proj=proj; // Get projection
opts+=layer.proj.Value()+" ";
{ // Get tics intervals
XYIntVal dx=xint,dy=yint;
opts+="-Bx"+ToString(dx.isauto?AutoStep(layer.proj.region.xb,layer.proj.region.xe):dx.val)+" -By"+ToString(dy.isauto?AutoStep(layer.proj.region.yb,layer.proj.region.ye):dy.val)+" ";
}
{ // Get xtics and ytics positions
std::string xt=xtics, yt=ytics;
std::string xaxis, yaxis;
TemplateComparator none("none"), both("both");
TemplateComparator up("up"), down("down");
TemplateComparator left("left"), right("right");
tolower(xt); tolower(yt);
if(none.Compare(xt)) xaxis="sn";
if(down.Compare(xt)) xaxis="Sn";
if( up.Compare(xt)) xaxis="sN";
if(both.Compare(xt)) xaxis="SN";
if( none.Compare(yt)) yaxis="we";
if( left.Compare(yt)) yaxis="We";
if(right.Compare(yt)) yaxis="wE";
if( both.Compare(yt)) yaxis="WE";
if(xaxis.empty()) {err="Incorrect xtics position "+xtics.Value(); goto fail;} // Unknown value of xtics
if(yaxis.empty()) {err="Incorrect xtics position "+ytics.Value(); goto fail;} // Unknown value of ytics
opts+="-B"+xaxis+yaxis+" ";
}
{ // Get domain and mark
std::string dom=domain;
std::string format;
TemplateComparator p("positive"),n("negative"),c("centered");
tolower(dom);
if(p.Compare(dom)) format="+ddd:mm:ss";
if(n.Compare(dom)) format="-ddd:mm:ss";
if(c.Compare(dom)) format="ddd:mm:ss";
if(format.empty()) {err="Incorrect domain value "+domain.Value(); goto fail;} // Unknown domain
if(mark) format+="F";
opts+="--FORMAT_GEO_MAP="+format+" ";
}
opts+="--FONT_ANNOT_PRIMARY="+font.Value().Value()+" "; // Get font
opts+="--MAP_ANNOT_OFFSET_PRIMARY="+ToString(offset.Value())+"c "; // Get offset
opts+="--MAP_FRAME_WIDTH="+ToString(framewidth.Value())+"c "; // Get framewidth
opts+="--MAP_FRAME_PEN="+framepen.Value().Value()+" "; // Get framepen
opts+="--MAP_TICK_LENGTH_PRIMARY="+ToString(ticklen.Value())+"c "; // Get ticklen
opts+="--MAP_TICK_PEN_PRIMARY="+tickpen.Value().Value()+" "; // Get tickpen
if(transp!=0) opts+="-t"+ToString(transp.Value())+" "; // Get transparency
opts+="--PS_TRANSPARENCY="+transpmodel.Value()+" "; // Get transparency model
// Get shift
layer.shiftx=rx;
layer.shifty=ry;
{ // Calling psbasemap
void* gmtapi;
std::string* draw=new std::string;
int ret;
gmtapi=GMT_Create_Session("DrawFrame",2,GMTMODE,0);
if(0==gmtapi) goto fail; // Can't create GMT GMT_Create_Session
ret=callgmtmodule(gmtapi,"psbasemap",opts,draw);
GMT_Destroy_Session(gmtapi);
if(0!=ret) {delete draw; goto fail;} // Psbasemap error
layer.data.reset(draw);
}
layer.creator="psbasemap "+opts;
return new ObjectGMTLayer(layer);
fail:
return new ObjectError("DrawFrame",err);
}

1064
modules/gmt/modgmt_func.h

File diff suppressed because it is too large Load Diff

168
modules/gmt/modgmt_gsfuncs.cpp

@ -0,0 +1,168 @@
#include <string.h>
#include <unistd.h>
#include "common.h"
#include "modgmt_gsfuncs.h"
struct gs_abilities_struct gs_abilities;
static int gs_callback_out(void *caller_handle, const char *buf, int len)
{
struct gs_runtime* r=static_cast<struct gs_runtime*>(caller_handle);
if(0!=r->out) r->out->append(buf,len);
return len;
}
static int gs_callback_err(void *caller_handle, const char *buf, int len)
{
struct gs_runtime* r=static_cast<struct gs_runtime*>(caller_handle);
if(0!=r->err) r->err->append(buf,len);
return len;
}
static int gs_callback_in(void *caller_handle, char *buf, int len)
{
struct gs_runtime* r=static_cast<struct gs_runtime*>(caller_handle);
if(r->pos>=r->in->length()) return 0;
size_t rem=r->in->length()-r->pos;
if(rem>static_cast<size_t>(len)) rem=len;
memcpy(buf,r->in->c_str()+r->pos,rem);
r->pos+=rem;
return rem;
}
struct gsworkthreadpars
{
struct gs_runtime* r;
gs_callback input_callback;
int fd;
const std::string* opts;
int ret;
};
// This function call gs_* in separate thread, because ghostscript write to stdout
static void* gsworkthread(void* x)
{
struct gsworkthreadpars* p=reinterpret_cast<struct gsworkthreadpars*>(x);
WordList wl;
if(-1==p->fd) wl=Split("-dSAFER -q -o/dev/null "+*(p->opts)+" -");
else wl=Split("-dSAFER -dDOINTERPOLATE -q -o/dev/fd/"+ToString(p->fd)+" "+*(p->opts)+" -");
int argc=wl.size()+1;
char** argv;
int i;
void* gs;
argv=new char*[argc];
argv[0]=new char[2]; argv[0][0]='G'; argv[0][1]=0;
i=1;
for(auto& opt: wl)
{
argv[i]=new char[opt.length()+1];
memcpy(argv[i],opt.c_str(),opt.length()+1);
i++;
}
p->ret=gsapi_new_instance(&gs,p->r);
if(p->ret<0) goto end;
gsapi_set_stdio(gs,p->input_callback?p->input_callback:gs_callback_in,gs_callback_out,gs_callback_err);
p->ret=gsapi_init_with_args(gs,argc,argv);
if(0==p->ret || e_Quit==p->ret) p->ret=gsapi_exit(gs);
else gsapi_exit(gs);
if(e_Quit==p->ret) p->ret=0;
gsapi_delete_instance(gs);
end:
close(p->fd);
for(i=0;i<argc;i++) delete[] argv[i];
delete[] argv;
return &p->ret;
}
// Main function for prepare and run ghostscript
static int GhostRun(const std::string& opts, struct gs_runtime* r, std::string* out, gs_callback input=0)
{
struct gsworkthreadpars wp;
int* pret;
int ret=0;
wp.r=r;
wp.input_callback=input;
wp.opts=&opts;
wp.ret=0;
wp.fd=-1;
if(0!=out)
{
int pipefd[2];
pthread_t wthr;
ssize_t br;
char buffer[4096];
if(0!=pipe(pipefd)) {ret=-1; goto end;}
wp.fd=pipefd[1];
out->erase();
pthread_create(&wthr,0,&gsworkthread,&wp);
do
{
br=read(pipefd[0],buffer,4096);
out->append(buffer,br);
} while(0!=br);
close(pipefd[0]);
out->shrink_to_fit();
pthread_join(wthr,reinterpret_cast<void**>(&pret));
}
else pret=reinterpret_cast<int*>(gsworkthread(&wp));
end:
if(0!=ret) return ret;
return *pret;
}
// This function run ghostscript with input data in string
int GhostRun(const std::string& opts, const std::string& input, std::string* sout, std::string* serr, std::string* out)
{
struct gs_runtime r;
r.in=&input;
r.pos=0;
r.out=sout;
r.err=serr;
return GhostRun(opts,&r,out);
}
// This function run ghostscript with input data provided by callback function
int GhostRun(const std::string& opts, gs_callback input, void* inputdata, std::string* sout, std::string* serr, std::string* out)
{
struct gs_runtime r;
r.indata=inputdata;
r.out=sout;
r.err=serr;
return GhostRun(opts,&r,out,input);
}
void CheckGhostscriptAbilities()
{
std::string out;
gs_abilities.havepdf=gs_abilities.havebbox=gs_abilities.havepngmono=gs_abilities.havepngmonod=gs_abilities.havepng16=gs_abilities.havepng256=gs_abilities.havepnggray=gs_abilities.havepng16m=gs_abilities.havejpeg=gs_abilities.havejpeggray=false;
if(0!=GhostRun("-sDEVICE=nullpage","devicenames ==",&out,0,0)) return;
WordList wl=Split(out,"[]/ \t");
for(auto& i:wl)
{
if("pdfwrite"==i) gs_abilities.havepdf=true;
if("bbox"==i) gs_abilities.havebbox=true;
if("pngmono"==i) gs_abilities.havepngmono=true;
if("pngmonod"==i) gs_abilities.havepngmonod=true;
if("png16"==i) gs_abilities.havepng16=true;
if("png256"==i) gs_abilities.havepng256=true;
if("pnggray"==i) gs_abilities.havepnggray=true;
if("png16m"==i) gs_abilities.havepng16m=true;
if("jpeg"==i) gs_abilities.havejpeg=true;
if("jpeggray"==i) gs_abilities.havejpeggray=true;
}
// gs_abilities.havepdf=(0==GhostRun("-sDEVICE=pdfwrite -dCompatibilityLevel=1.4","gsave 1 0 0 setrgbcolor 5 setlinewidth 100 100 moveto 200 200 lineto stroke grestore showpage",0,0,0));
}

42
modules/gmt/modgmt_gsfuncs.h

@ -0,0 +1,42 @@
#ifndef MODGMT_GSFUNCS_H
#define MODGMT_GSFUNCS_H
#include <string>
#include <ghostscript/iapi.h>
#include <ghostscript/ierrors.h>
#ifndef e_Quit
#define e_Quit (-101)
#endif
struct gs_abilities_struct
{
bool havepngmono,havepngmonod,havepng16,havepng256,havepnggray,havepng16m;
bool havejpeg,havejpeggray;
bool havepdf,havebbox;
};
extern struct gs_abilities_struct gs_abilities;
struct gs_runtime
{
union
{
struct
{
const std::string* in;
uint pos;
};
void* indata;
};
std::string* out;
std::string* err;
};
typedef int (*gs_callback)(void *caller_handle, char *buf, int len);
int GhostRun(const std::string& opts, const std::string& input, std::string* sout, std::string* serr, std::string* out);
int GhostRun(const std::string& opts, gs_callback input, void* inputdata, std::string* sout, std::string* serr, std::string* out);
void CheckGhostscriptAbilities();
#endif

135
modules/gmt/modgmt_internals.cpp

@ -0,0 +1,135 @@
#include <pthread.h>
#include "common.h"
#include "modgmt_internals.h"
std::string header,footer;
// Workaround exit() in GMT_Call_Module. May need because return mode of gmt api is not very reliable
#if defined MODGMT_WORKAROUND_EXIT
const int GMTMODE=GMT_SESSION_NORMAL;
#else
const int GMTMODE=GMT_SESSION_NOEXIT;
#endif
#if defined MODGMT_WORKAROUND_EXIT
// Exit handler.
static void gmtonexithandler(int ret, void* x)
{
reinterpret_cast<struct gmtworkthreadpars*>(x)->ret=ret;
close(reinterpret_cast<struct gmtworkthreadpars*>(x)->fd);
pthread_exit(&(reinterpret_cast<struct gmtworkthreadpars*>(x)->ret));
}
#endif
// This function call GMT_Call_Module in separate thread. It just a hack to workaround absence of callbacks.
static void* gmtworkthread(void* x)
{
struct gmtworkthreadpars* p=reinterpret_cast<struct gmtworkthreadpars*>(x);
#if defined MODGMT_WORKAROUND_EXIT
on_exit(gmtonexithandler,x);
#endif
GMT_Append_Option(p->api,GMT_Make_Option(p->api,'>',const_cast<char*>(("/dev/fd/"+std::to_string(p->fd)).c_str())),p->opts);
p->ret=GMT_Call_Module(p->api,p->module,GMT_MODULE_OPT,p->opts);
#if defined MODGMT_WORKAROUND_EXIT
exit(p->ret);
#endif
close(p->fd);
return &p->ret;
}
// Wrapper for GMT_Call_Module, res is output.
int callgmtmodule(void *api, const char *module, struct GMT_OPTION *opts, std::string* res, gmt_filter filt, void* filtpar)
{
int pipefd[2];
pthread_t wthr;
struct gmtworkthreadpars p;
int *pret;
int ret=0;
if(0!=pipe(pipefd)) {ret=-1; goto end;}
p.api=api;
p.module=module;
p.opts=opts;
p.fd=pipefd[1];
pthread_create(&wthr,0,&gmtworkthread,&p);
res->erase();
(*filt)(pipefd[0],res,filtpar);
res->shrink_to_fit();
pthread_join(wthr,reinterpret_cast<void**>(&pret));
end:
if(0!=ret) return ret;
return *pret;
}
// Overloaded variant with opts as std::string
int callgmtmodule(void *api, const char *module, const std::string& opts, std::string* res, gmt_filter filt, void* filtpar)
{
struct GMT_OPTION *gopts;
int ret;
gopts=str2options(api,opts);
ret=callgmtmodule(api,module,gopts,res,filt,filtpar);
GMT_Destroy_Options(api,&gopts);
return ret;
}
// Overloaded variant with opts as char*
int callgmtmodule(void *api, const char *module, const char* opts, std::string* res, gmt_filter filt, void* filtpar)
{
struct GMT_OPTION *gopts;
int ret;
gopts=str2options(api,opts);
ret=callgmtmodule(api,module,gopts,res,filt,filtpar);
GMT_Destroy_Options(api,&gopts);
return ret;
}
// Calculate real width and height of projection. If height!=0 recalculate width accordingly.
bool ProjectionRealSize(struct gmt_projection& p, double height)
{
// Decart projection is special case
if(gmt_projection::XY==p.proj)
{
p.rwidth=p.width;
p.rheight=p.x.height;
return true;
}
void* gmtapi;
int ret=0;
std::string wh;
double w,h;
double sw=p.width;
WordList wl;
WordList::const_iterator cw;
gmtapi=GMT_Create_Session("ProjectionRealSize",2,GMTMODE,0);
if(0==gmtapi) return false;
p.width=1.0;
ret=callgmtmodule(gmtapi,"mapproject",p.Value()+" -W",&wh);
p.width=sw;
GMT_Destroy_Session(gmtapi);
if(0!=ret) return false;
wl=Split(wh," \t\n");
if(2!=wl.size()) return false;
cw=wl.begin();
if(!str2double(*cw,&w)) return false;
cw++;
if(!str2double(*cw,&h)) return false;
if(height>0.0)
{
p.width=height/h;
p.rwidth=w*height/h;
p.rheight=height;
}
else
{
p.width=sw/w;
p.rwidth=sw;
p.rheight=h*sw/w;
}
return true;
}

52
modules/gmt/modgmt_internals.h

@ -0,0 +1,52 @@
#ifndef MODGMT_INTERNALS_H
#define MODGMT_INTERNALS_H
#include <gmt.h>
#include <string.h>
#include "modgmt_filters.h"
#include "modgmt_structs.h"
// here we save header and footer of gmt-produced eps files
extern std::string header,footer;
extern const int GMTMODE;
// Parameters for working thread
struct gmtworkthreadpars
{
void* api;
const char* module;
struct GMT_OPTION* opts;
int fd;
int ret;
};
// Calling GMT module with filtering
int callgmtmodule(void *api, const char *module, struct GMT_OPTION *opts, std::string* res, gmt_filter filt=gmt_filter_default, void* filtpar=0);
int callgmtmodule(void *api, const char *module, const std::string& opts, std::string* res, gmt_filter filt=gmt_filter_default, void* filtpar=0);
int callgmtmodule(void *api, const char *module, const char* opts, std::string* res, gmt_filter filt=gmt_filter_default, void* filtpar=0);
// Workaround non-const pointer in GMT_Create_Options
inline struct GMT_OPTION* str2options(void *api, const char* str, size_t size=0)
{
static char default_gmt_options[]="--GMT_HISTORY=f --PS_LINE_CAP=round --PS_LINE_JOIN=round --GMT_COMPATIBILITY=5 --PS_PAGE_ORIENTATION=portrait --PS_IMAGE_COMPRESS=deflate,9 --PROJ_LENGTH_UNIT=cm";
char* t;
if(0==size) t=strdup(str);
else
{
t=static_cast<char*>(malloc(size+1));
memcpy(t,str,size);
t[size]=0;
}
struct GMT_OPTION* opts=GMT_Create_Options(api,0,t);
GMT_Append_Option(api,GMT_Create_Options(api,0,default_gmt_options),opts);
free(t);
return opts;
}
inline struct GMT_OPTION* str2options(void *api, const std::string& str)
{
return str2options(api,str.data(),str.length());
}
// Calculate real width and height of projection. If height!=0 recalculate width accordingly.
bool ProjectionRealSize(struct gmt_projection& p, double height=0.0);
#endif

460
modules/gmt/modgmt_map.cpp

@ -0,0 +1,460 @@
#include <regex>
#include <stack>
#include "modgmt_map.h"
#include "modgmt_func.h"
#include "modgmt_gsfuncs.h"
struct input_runtime
{
std::list<std::shared_ptr<std::string> >::const_iterator ci,end;
size_t pos;
};
static int gs_bbox_callback(void *caller_handle, char *buf, int len)
{
struct input_runtime* r=static_cast<struct input_runtime*>(static_cast<struct gs_runtime*>(caller_handle)->indata);
size_t buflen=static_cast<size_t>(len);
size_t inbuf=0;
if(r->ci==r->end) return 0;
while(inbuf!=buflen)
{
if((*r->ci)->length()-r->pos>=buflen-inbuf) // Remainder of string greater or equal remainder of buffer
{
// Just copy part of string to buffer
memcpy(buf+inbuf,(*r->ci)->c_str()+r->pos,buflen-inbuf);
r->pos+=buflen-inbuf;
inbuf=buflen;
}
else // Remainder of string lesser then remainder of buffer
{
// Copy remainder of string to buffer
memcpy(buf+inbuf,(*r->ci)->c_str()+r->pos,(*r->ci)->length()-r->pos);
inbuf+=(*r->ci)->length()-r->pos;
// Go to next string
r->ci++; r->pos=0;
if(r->ci==r->end) break; // No more strings, leave.
}
}
return inbuf;
}
static inline int eps2pdf(const std::string& eps, std::string* pdf, double res, uint ta=4, uint ga=4)
{
if(!gs_abilities.havepdf) return 1; // No pdf support
return GhostRun("-r"+ToString(res)+" -dEPSCrop -dTextAlphaBits="+ToString(ta)+" -dGraphicAlphaBits="+ToString(ga)+" -dNOPLATFONT -dSubsetFonts=true -dEmbedAllFonts=true -dAutoFilterColorImages=false -dColorImageFilter=/FlateEncode -dAutoFilterGrayImages=false -dGrayImageFilter=/FlateEncode -dAutoFilterMonoImages=false -dMonoImageFilter=/CCITTFaxEncode -sDEVICE=pdfwrite -dMaxInlineImageSize=0 -c .setpdfwrite <</NeverEmbed [ ]>> setdistillerparams",eps,0,0,pdf);
}
// Creating eps map from sequence of layers
/*
Input:
If first argument is a string, then this string is a title of map (%%Title parameter in eps file).
Other arguments are sequence of pairs and layers.
Pairs can be x, y, xr, yr, xg, yg, xgr, ygr.
x and y set shift for next layer in cm. This local shift overrides global (setted by xg, yg).
xr and yr do the same but respect to global shift.
xg and yg set global shift (for all subsequent layers).
xgr and ygr add corresponding values to global shift.
Resulted shift is added to own shift of layer (setted by function LayerShift).
*/
const ObjectBase* GMT_Map(const ObjectList* input)
{
std::string err;
std::string title;
double xg,yg,x,y;
bool xislocal=false, yislocal=false;
std::list<std::shared_ptr<std::string> > data;
xg=yg=x=y=0.0;
// FIXME: Workaround ghostscript bug 202735
// See http://bugs.ghostscript.com/show_bug.cgi?id=202735
data.emplace_back(new std::string("4000 4000 translate"));
data.emplace_back(new std::string(header));
for(auto i=input->begin(); i!=input->end(); ++i)
{
// Check if first argument is string
if(input->begin()==i)
{
OBType<ObjectString> s(*i);
if(s)
{
title=s->Value();
continue;
}
}
// Check if argument is pair
{
OBType<ObjectPair> p(*i);
if(p)
{
std::string name=p->Name();
tolower(name);
Base2Double d;
bool suc=true;
double val=d.Convert(p->Value(),&suc,err);
if(!suc) goto fail; // Conversion failed
suc=false;
if("x"==name) {suc=true; xislocal=true; x=val;}
if("y"==name) {suc=true; yislocal=true; y=val;}
if("xr"==name) {suc=true; xislocal=true; x=val+xg;}
if("yr"==name) {suc=true; yislocal=true; y=val+yg;}
if("xg"==name) {suc=true; xg=val;}
if("yg"==name) {suc=true; yg=val;}
if("xgr"==name) {suc=true; xg+=val;}
if("ygr"==name) {suc=true; yg+=val;}
if(!suc) {err="Unknown parameter name: "+p->Name(); goto fail;} // Unknown name
continue;
}
}
// Check if argument is layer
{
OBType<ObjectGMTLayer> l(*i);
if(l)
{
struct gmt_layer layer=l->Data();
layer.shiftx+=(xislocal?x:xg);
layer.shifty+=(yislocal?y:yg);
xislocal=yislocal=false;
if(layer.Shifted())
{
data.emplace_back(new std::string(layer.BeginShift()));
data.emplace_back(layer.data);
data.emplace_back(new std::string(layer.EndShift()));
}
else data.emplace_back(layer.data);
continue;
}
}
err="Unknown argument type: "+i->Type();
goto fail; // Unknown type of argument
}
data.emplace_back(new std::string(footer));
// Calculate bounding box
int64_t bblx,bbly,bbrx,bbry;
{
std::string bboxes;
struct input_runtime in={data.begin(),data.end(),0};
int ret;
ret=GhostRun("-sDEVICE=bbox",gs_bbox_callback,&in,0,&bboxes,0); // Bounding box is writed on stderr
if(0!=ret) {err="Can't determine BoundingBox"; goto fail;} // Something wrong
std::smatch m;
std::smatch::const_iterator ci;
std::regex r("%%BoundingBox:[[:space:]]+(-?[0-9]+)[[:space:]]+(-?[0-9]+)[[:space:]]+(-?[0-9]+)[[:space:]]+(-?[0-9]+)");
bool ok=true;
std::regex_search(bboxes,m,r);
if(5!=m.size()) {err="Something go wrong, this is strange and scary"; goto fail; }// This is strange and scary
ci=m.cbegin();
ci++; // Skip full match
ok=ok && str2int(*ci++,&bblx);
ok=ok && str2int(*ci++,&bbly);
ok=ok && str2int(*ci++,&bbrx);
ok=ok && str2int(*ci++,&bbry);
if(!ok) {err="Unexpected error!"; goto fail;} // Unexpected!
// FIXME: Workaround ghostscript bug 202735
// We add 5 points to each margin because ghostscript does'nt count linewidths when calculate bounding box
bblx-=4005;
bbly-=4005;
bbrx-=3995;
bbry-=3995;
}
data.pop_front();
// Creating eps file
{
std::string curtime;
{
const size_t bufsize=1024;
char buf[bufsize];
time_t sec;
struct tm t;
sec=time(0);
localtime_r(&sec,&t);
curtime.assign(buf, strftime(buf,bufsize,"%F %T %Z",&t));
}
std::string* eps=new std::string("%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: ");
*eps+=ToString(bblx)+" "+ToString(bbly)+" "+ToString(bbrx)+" "+ToString(bbry);
*eps+="\n%%Title: "+(title.empty()?std::string("untitled"):title);
*eps+="\n%%Creator: gmt_makemap\n%%CreationDate: "+curtime;
*eps+="\n%%LanguageLevel: 2\n%%Orientation: Portrait\n%%EndComments\n";
for(auto d: data) *eps+=*d;
return new ObjectGMTMap(eps,bblx,bbly,bbrx,bbry);
}
fail:
return new ObjectError("Map",err);
}
// Creating pdf from eps
/*
Input:
One argument must be GMTMap.
Optionally, resolution can be specified by pair with name resolution, res or r and double value. Default is 720.
*/
const ObjectBase* GMT_Convert2PDF(const ObjectList* input)
{
std::string err;
RNFPar<SearchGMTMap,ObjectGMTMap> map("map");
ONPar<Base2PosD> r("resolution",720);
ParseNamedParameters params(input,map,r);
const ObjectGMTMap* m=map;
if(!gs_abilities.havepdf) {err="No PDF support in Ghostscript"; goto fail;} // No pdf support
if(!params) {err=params.Error(); goto fail;} // Parsing error
{
std::string* out=new std::string;
int ret=eps2pdf(*(m->pValue()),out,r);
if(0!=ret) {err="Conversion of EPS to PDF failed"; delete out; goto fail; } // Something wrong
return new ObjectGMTMapPDF(out,m->Bblx(),m->Bbly(),m->Bbrx(),m->Bbry());
}
fail:
return new ObjectError("EPS2PDF",err);
}
// Creating png from eps or pdf
/*
Input:
One argument must be GMTMap or GMTMapPDF.
Other parameter are optional name-value pairs.
resolution, res or r - output resolution (pixels/inch). Default: 300.
Instead of resolution, width (width or w) or height (height or h) in pixels may be specified. Resolution, width and height are mutually exclusive. Large value of resolution may result in ghostscript error.
colorspace, cspace or cs - is a string, one of "mono" or "monochrome" (black and white image), "monodiffused" or "monod" (black and white image made from grayscale by error diffusion), "16c" or "16" (16 colors), "256c" or "256" (256 colors), "gray", "grey" or "g" (256 shades of gray), "full", "8bit" or "color" (8-bit per component rgb). Some of these values may return error, this depends on ghostscript setup in system. Default: "color".
For colorspaces "monod", "color" and "gray" may be also specified parameter downscale (dscale or ds) with small integer value (from 1 to 10). The image initially rendered in dscale*res resolution and downscaling to res resolution. This increase output quality (lines and contours are more "soft"), but may result in ghostscript error if rendering resolution is too high.
textantialiasing or taa - is a string, can be "none" ("no"), "small" ("s") or "full" ("f"). Controls text antialiasing. Default: "full".
graphicsantialiasing or gaa - is a string, can be "none", "small" or "full". Controls graphics antialiasing. Default: "full".
antialiasing or aa - can be used to set graphics and text antialiasing simultaneously.
*/
const ObjectBase* GMT_Convert2PNG(const ObjectList* input)
{
std::string gsdev,err;
double res;
ONPar<Base2PosD> r("resolution",300),ds("d[own]scale",1.0);
ONPar<Base2Pos> w("width"),h("height");
ONPar<Base2StringD> cs("c[olor][ ]space","color"),taa("t[ext]a[nti]aliasing","full"),gaa("g[raphics]a[nti]aliasing","full"),aa("a[nti]aliasing","full");
const ObjectGMTMap* me;
const ObjectGMTMapPDF* mp;
const GMTMap* m;
uint aat,aag;
uint dscale;
if(!gs_abilities.havepdf) {err="No PDF support in Ghostscript"; goto fail;} // No pdf support, so, no png
// Search map
{
RNFPar<SearchGMTMap,ObjectGMTMap> mapeps("map");
RNFPar<SearchGMTMapPDF,ObjectGMTMap> mappdf("map");
ParseNamedParameters peps(input,mapeps);
ParseNamedParameters ppdf(input,mappdf);
if(!(peps || ppdf)) {err="Error parsing map parameter"; goto fail;}
me=mapeps; mp=mappdf;
if(nullptr==me && nullptr==mp) {err="Map parameter not specified"; goto fail;} // This must never happened
if(nullptr!=me) m=me; else m=mp;
}
// Parse other parameters
{
ParseNamedParameters params(input,r,ds,w,h,cs,taa,gaa,aa);
if(!params) {err=params.Error(); goto fail;}
}
// Determine resolution
{
// Check existence
if( ( r.Exist() && w.Exist() ) || ( r.Exist() && h.Exist() ) || ( w.Exist() && h.Exist() )) {err="Only one of resolution, width or height may be specified"; goto fail;} // Only one parameter allowed
if(r.Exist()) res=r;
if(w.Exist()) res=w*72.0/(m->Bbrx()-m->Bblx());
if(h.Exist()) res=h*72.0/(m->Bbry()-m->Bbly());
}
// Color model
{
std::string cspace=cs;
tolower(cspace);
if(("mono" ==cspace || "monochrome" ==cspace ) && gs_abilities.havepngmono) gsdev="pngmono";
if(("monod"==cspace || "monodiffused"==cspace ) && gs_abilities.havepngmonod) gsdev="pngmonod";
if(("16c" ==cspace || "16" ==cspace ) && gs_abilities.havepng16) gsdev="png16";
if(("256c" ==cspace || "256" ==cspace ) && gs_abilities.havepng256) gsdev="png256";
if(("gray" ==cspace || "grey" ==cspace || "g" ==cspace) && gs_abilities.havepnggray) gsdev="pnggray";
if(("full" ==cspace || "8bit" ==cspace || "color"==cspace) && gs_abilities.havepng16m) gsdev="png16m";
if(gsdev.empty()) {err="Colorspace "+cs.Value()+" is bad or unsupported"; goto fail;} // Incorrect value
}
// Antialiasing
{
std::string aags=aa,aats=aa;
if(taa.Exist()) aats=taa;
if(gaa.Exist()) aags=gaa;
tolower(aags); tolower(aats);
if("none" ==aags || "no"==aags || "n"==aags) aag=1;
if("none" ==aats || "no"==aats || "n"==aats) aat=1;
if("small"==aags || "s" ==aags ) aag=2;
if("small"==aats || "s" ==aats ) aat=2;
if("full" ==aags || "f" ==aags ) aag=4;
if("full" ==aats || "f" ==aats ) aat=4;
if(0==aat) {err="Incorrect value for text antialiasing: "+aats; goto fail;}
if(0==aag) {err="Incorrect value for graphics antialiasing: "+aags; goto fail;}
}
// Downscale
if("pngmonod"==gsdev || "pnggray"==gsdev || "png16m"==gsdev)
{
dscale=static_cast<uint>(ds);
if(dscale<1 || dscale>10) {err="Downscale must be in interval from 1 to 10"; goto fail;}
res*=dscale;
}
// Go!
{
std::string* pdf=0;
int ret;
if(nullptr!=me)
{
pdf=new std::string;
ret=eps2pdf(*(me->pValue()),pdf,r,aat,aag);
if(0!=ret) {err="Can't convert EPS to PDF"; delete pdf; goto fail; } // Something wrong
}
{
std::string* out=new std::string;
ret=GhostRun("-r"+ToString(res)+" -dTextAlphaBits="+ToString(aat)+" -dGraphicAlphaBits="+ToString(aag)+" -sDEVICE="+gsdev+((dscale>1)?(" -dDownScaleFactor="+ToString(dscale)):""),((0!=pdf)?*pdf:*(mp->pValue())),0,0,out);
if(0!=pdf) delete pdf;
if(0!=ret) {err="Can't convert PDF to PNG"; delete out; goto fail; } // Something wrong
return new ObjectGMTImage(out);
}
}
fail:
return new ObjectError("EPS2PNG",err);
}
// Creating jpeg from eps or pdf
/*
Input:
One argument must be GMTMap or GMTMapPDF.
Other parameter are optional name-value pairs.
resolution, res or r - output resolution (pixels/inch). Default: 300.
Instead of resolution, width (width or w) or height (height or h) in pixels may be specified. Resolution, width and height are mutually exclusive. Large value of resolution may result in ghostscript error.
colorspace, cspace or cs - is a string, one "gray", "grey" or "g" (256 shades of gray), "full", "8bit" or "color" (8-bit per component rgb). Some of these values may return error, this depends on ghostscript setup in system. Default: "color".
quality, qual or q - JPEG quality level, integer from 0 to 100. Default: 75.
textantialiasing or taa - is a string, can be "none" ("no"), "small" ("s") or "full" ("f"). Controls text antialiasing. Default: "full".
graphicsantialiasing or gaa - is a string, can be "none", "small" or "full". Controls graphics antialiasing. Default: "full".
antialiasing or aa - can be used to set graphics and text antialiasing simultaneously.
*/
const ObjectBase* GMT_Convert2JPG(const ObjectList* input)
{
std::string gsdev,err;
ONPar<Base2PosD> r("resolution",300);
ONPar<Base2Pos> w("width"),h("height");
ONPar<Base2StringD> cs("c[olor][ ]space","color"),taa("t[ext]a[nti]aliasing","full"),gaa("g[raphics]a[nti]aliasing","full"),aa("a[nti]aliasing","full");
ONPar<Base2NonNegD> q("quality",75);
const ObjectGMTMap* me;
const ObjectGMTMapPDF* mp;
const GMTMap* m;
double res;
uint aat,aag;
uint qual;
if(!gs_abilities.havepdf) {err="No PDF support in Ghostscript"; goto fail;} // No pdf support, so, no jpeg
// Search map
{
RNFPar<SearchGMTMap,ObjectGMTMap> mapeps("map");
RNFPar<SearchGMTMapPDF,ObjectGMTMap> mappdf("map");
ParseNamedParameters peps(input,mapeps);
ParseNamedParameters ppdf(input,mappdf);
if(!(peps || ppdf)) {err="Error parsing map parameter"; goto fail;}
me=mapeps; mp=mappdf;
if(nullptr==me && nullptr==mp) {err="Map parameter not specified"; goto fail;} // This must never happened
if(nullptr!=me) m=me; else m=mp;
}
// Parse other parameters
{
ParseNamedParameters params(input,r,q,w,h,cs,taa,gaa,aa);
if(!params) {err=params.Error(); goto fail;}
}
// Determine resolution
{
// Check existence
if( ( r.Exist() && w.Exist() ) || ( r.Exist() && h.Exist() ) || ( w.Exist() && h.Exist() )) {err="Only one of resolution, width or height may be specified"; goto fail;} // Only one parameter allowed
if(r.Exist()) res=r;
if(w.Exist()) res=w*72.0/(m->Bbrx()-m->Bblx());
if(h.Exist()) res=h*72.0/(m->Bbry()-m->Bbly());
}
// Color model
{
std::string cspace=cs;
tolower(cspace);
if(("gray"==cspace || "grey"==cspace || "g" ==cspace) && gs_abilities.havejpeggray) gsdev="jpeggray";
if(("full"==cspace || "8bit"==cspace || "color"==cspace) && gs_abilities.havejpeg) gsdev="jpeg";
if(gsdev.empty()) {err="Colorspace "+cs.Value()+" is bad or unsupported"; goto fail;} // Incorrect value
}
// Antialiasing
{
std::string aags=aa,aats=aa;
if(taa.Exist()) aats=taa;
if(gaa.Exist()) aags=gaa;
tolower(aags); tolower(aats);
if("none" ==aags || "no"==aags || "n"==aags) aag=1;
if("none" ==aats || "no"==aats || "n"==aats) aat=1;
if("small"==aags || "s" ==aags ) aag=2;
if("small"==aats || "s" ==aats ) aat=2;
if("full" ==aags || "f" ==aags ) aag=4;
if("full" ==aats || "f" ==aats ) aat=4;
if(0==aat) {err="Incorrect value for text antialiasing: "+aats; goto fail;}
if(0==aag) {err="Incorrect value for graphics antialiasing: "+aags; goto fail;}
}
// Quality
{
qual=static_cast<uint>(q);
if(q>100) {err="JPEG quality must not exceed 100"; goto fail;} // Error
}
// Go!
{
std::string* pdf=0;
int ret;
if(nullptr!=me)
{
pdf=new std::string;
ret=eps2pdf(*(me->pValue()),pdf,r,aat,aag);
if(0!=ret) {err="Can't convert EPS to PDF"; delete pdf; goto fail; } // Something wrong
}
{
std::string* out=new std::string;
ret=GhostRun("-r"+ToString(res)+" -dTextAlphaBits="+ToString(aat)+" -dGraphicAlphaBits="+ToString(aag)+" -sDEVICE="+gsdev+" -dJPEGQ="+ToString(qual),((0!=pdf)?*pdf:*(mp->pValue())),0,0,out);
if(0!=pdf) delete pdf;
if(0!=ret) {err="Can't convert PDF to JPEG"; delete out; goto fail; } // Something wrong
return new ObjectGMTImage(out);
}
}
fail:
return new ObjectError("EPS2JPEG",err);
}

112
modules/gmt/modgmt_map.h

@ -0,0 +1,112 @@
#ifndef MODGMT_MAP_H
#define MODGMT_MAP_H
#include <cmath>
#include "common.h"
class GMTBlob: public ObjectBase
{
GMTBlob()=delete;
protected:
std::shared_ptr<std::string> data;
GMTBlob(const GMTBlob& b)=default;
GMTBlob(GMTBlob&& b)=default;
GMTBlob(const GMTBlob* p):data(p->data) {}
GMTBlob(std::string* s):data(s) {}
public:
// Pure virtual overrides
bool Print() const override
{
COUT(NORMAL)<<std::endl<<"Object type: "<<Type()<<std::endl;
return true;
}
const int8_t* Blob(size_t* size) const override
{
*size=data->size();
return reinterpret_cast<const int8_t*>(data->data());
}
// Data access
const std::string* pValue() const {return data.get();}
};
class GMTMap: public GMTBlob
{
protected:
int32_t bblx,bbly,bbrx,bbry;
GMTMap(const GMTMap* p):GMTBlob(p),bblx(p->bblx),bbly(p->bbly),bbrx(p->bbrx),bbry(p->bbry) {};
GMTMap(std::string* s, int nbblx, int nbbly, int nbbrx, int nbbry):GMTBlob(s),bblx(nbblx),bbly(nbbly),bbrx(nbbrx),bbry(nbbry) {};
public:
int32_t Bblx() const {return bblx;}
int32_t Bbly() const {return bbly;}
int32_t Bbrx() const {return bbrx;}
int32_t Bbry() const {return bbry;}
};
class ObjectGMTMapPDF: public GMTMap
{
ObjectGMTMapPDF(const ObjectGMTMapPDF* p):GMTMap(p) {};
public:
ObjectGMTMapPDF(std::string* s, int nbblx, int nbbly, int nbbrx, int nbbry):GMTMap(s,nbblx,nbbly,nbbrx,nbbry) {};
// Pure virtual overrides
ObjectBase* Copy() const override {return new ObjectGMTMapPDF(this);}
std::string Type() const override {return "GMTMapPDF";}
};
class ObjectGMTMap: public GMTMap
{
ObjectGMTMap(const ObjectGMTMap* p):GMTMap(p) {};
public:
ObjectGMTMap(std::string* s, int nbblx, int nbbly, int nbbrx, int nbbry):GMTMap(s,nbblx,nbbly,nbbrx,nbbry) {};
// Pure virtual overrides
ObjectBase* Copy() const override {return new ObjectGMTMap(this);}
std::string Type() const override {return "GMTMap";}
};
class ObjectGMTImage: public GMTBlob
{
ObjectGMTImage(const ObjectGMTImage* p):GMTBlob(p) {};
public:
ObjectGMTImage(std::string* s):GMTBlob(s) {};
// Pure virtual overrides
ObjectBase* Copy() const override {return new ObjectGMTImage(this);}
std::string Type() const override {return "GMTImage";}
};
// Convertor to search object
template<class Object>
class SearchObject
{
public:
using ValueType=const Object*;
template<class... Args>
ValueType Convert(const ObjectBase* ob, bool* res, std::string& err, Args... args)
{
OBType<Object> gp(ob);
switch(gp.Error())
{
case(OBTypeErr::OK): return gp;
case(OBTypeErr::NULLPTR): {err="Can't convert zero ObjectBase pointer to something meaningfull"; break;}
case(OBTypeErr::TYPEMISMATCH): {err=ob->Type()+" is not correct: type mismatch"; break;}
}
*res=false;
return nullptr;
}
};
// Convertor to search GMTMap objects
using SearchGMTMap=SearchObject<ObjectGMTMap>;
using SearchGMTMapPDF=SearchObject<ObjectGMTMapPDF>;
// Creating eps map from set of layers
const ObjectBase* GMT_Map(const ObjectList* input);
// Converting map to pdf
const ObjectBase* GMT_Convert2PDF(const ObjectList* input);
// Converting map to png
const ObjectBase* GMT_Convert2PNG(const ObjectList* input);
// Converting map to jpeg
const ObjectBase* GMT_Convert2JPG(const ObjectList* input);
#endif

97
modules/gmt/modgmt_objects.cpp

@ -0,0 +1,97 @@
#include "modgmt_objects.h"
template<> const std::string ObjectGMTCoord::type="GMTCoord";
template<> const std::string ObjectGMTRegion::type="GMTRegion";
template<> const std::string ObjectGMTProjection::type="GMTProjection";
template<> const std::string ObjectGMTColor::type="GMTColor";
template<> const std::string ObjectGMTDash::type="GMTDash";
template<> const std::string ObjectGMTPen::type="GMTPen";
template<> const std::string ObjectGMTFont::type="GMTFont";
template<> const std::string ObjectGMTLayer::type="GMTLayer";
template<> const int8_t* ObjectGMTLayer::Blob(size_t* size) const
{
if(s.Shifted())
{
std::string b=s.BeginShift();
std::string e=s.EndShift();
int8_t* p=new int8_t[b.size()+s.data->size()+e.size()];
memcpy(p,b.data(),b.size());
memcpy(p+b.size(),s.data->data(),s.data->size());
memcpy(p+b.size()+s.data->size(),e.data(),e.size());
return p;
}
else
{
*size=s.data->size();
return reinterpret_cast<const int8_t*>(s.data->data());
}
}
template<> void ObjectGMTLayer::DeallocBlob(const int8_t* ptr) const
{
if(s.Shifted()) delete[] ptr;
}
std::map<std::string,gmt_projection::projection> gmt_projection::projnames;
void gmt_projection::FillProjNames()
{
projnames["(xy|decart)"]=XY;
projnames["c[ylindrical][( |_)]equidistant"]=CYL_EQU;
projnames["mercator"]=MERCATOR;
projnames["t[ransverse][( |_)]mercator"]=TRANSMERCATOR;
projnames["o[blique][( |_)]mercator"]=OBLIQMERCATOR;
projnames["cassini"]=CASSINI;
projnames["c[ylindrical][( |_)]eq[ua(l|l-)]area"]=CYL_EQA;
projnames["[c[ylindrical][( |_)]]miller"]=MILLER;
projnames["c[ylindrical][( |_)]stereographic"]=CYL_STERE;
}
bool gmt_font::FillFontNames()
{
families.insert("Helvetica");
families.insert("Helvetica-Bold");
families.insert("Helvetica-Oblique");
families.insert("Helvetica-BoldOblique");
families.insert("Times-Roman");
families.insert("Times-Bold");
families.insert("Times-Italic");
families.insert("Times-BoldItalic");
families.insert("Courier");
families.insert("Courier-Bold");
families.insert("Courier-Oblique");
families.insert("Courier-BoldOblique");
families.insert("Symbol");
families.insert("AvantGarde-Book");
families.insert("AvantGarde-BookOblique");
families.insert("AvantGarde-Demi");
families.insert("AvantGarde-DemiOblique");
families.insert("Bookman-Demi");
families.insert("Bookman-DemiItalic");
families.insert("Bookman-Light");
families.insert("Bookman-LightItalic");
families.insert("Helvetica-Narrow");
families.insert("Helvetica-Narrow-Bold");
families.insert("Helvetica-Narrow-Oblique");
families.insert("Helvetica-Narrow-BoldOblique");
families.insert("NewCenturySchlbk-Roman");
families.insert("NewCenturySchlbk-Italic");
families.insert("NewCenturySchlbk-Bold");
families.insert("NewCenturySchlbk-BoldItalic");
families.insert("Palatino-Roman");
families.insert("Palatino-Italic");
families.insert("Palatino-Bold");
families.insert("Palatino-BoldItalic");
families.insert("ZapfChancery-MediumItalic");
families.insert("ZapfDingbats");
default_family="Times-Roman";
return true;
}
const double gmt_projection::default_width=10.0;
const double gmt_pen::default_width=1.0;
const double gmt_dash::default_width=gmt_pen::default_width;
const double gmt_font::default_size=12.0;
std::string gmt_font::default_family;
std::set<std::string> gmt_font::families;

138
modules/gmt/modgmt_objects.h

@ -0,0 +1,138 @@
#ifndef MODGMT_OBJECTS_H
#define MODGMT_OBJECTS_H
#include <cmath>
#include <map>
#include <string.h>
#include <type_traits>
#include <vector>
#include "common.h"
#include "modgmt_structs.h"
template<class GMTStruct>
class ObjectGMTClass: public ObjectBase
{
static_assert(std::is_base_of<gmt_struct,GMTStruct>::value,"Template parameter of ObjectGMTClass must be gmt_struct-derived type");
const static std::string type;
GMTStruct s;
public:
// Constructor
ObjectGMTClass(const GMTStruct& n):s(n) {}
// Pure virtual overrides
ObjectBase* Copy() const override {return new ObjectGMTClass(s);}
bool Print() const override
{
COUT(NORMAL)<<std::endl<<"Object type: "<<Type()<<std::endl;
COUT(NORMAL)<<"Value: "<<Value()<<std::endl;
return true;
}
std::string Type() const override {return type;}
const int8_t* Blob(size_t* size) const override { *size=0; return 0; }
void DeallocBlob(const int8_t* ptr) const override {};
// Own functions
ObjectBase* Get(const std::string& gname) const
{
std::string name=gname;
tolower(name);
if("value"==name) return new ObjectString(Value());
return OGet(name);
}
std::string Value() const {return s.Value();}
GMTStruct Data() const {return s;}
private:
// This is class-dependent Get function
ObjectBase* OGet(const std::string& name) const {return 0;}
};
// GMTCoord
typedef ObjectGMTClass<struct gmt_coord> ObjectGMTCoord;
template<> inline ObjectBase* ObjectGMTCoord::OGet(const std::string& name) const
{
if("n"==name || "number"==name) return new ObjectReal(s);
return 0;
}
// GMTRegion
typedef ObjectGMTClass<struct gmt_region> ObjectGMTRegion;
template<> inline ObjectBase* ObjectGMTRegion::OGet(const std::string& name) const
{
if("xb"==name) return new ObjectGMTCoord(s.xb);
if("xe"==name) return new ObjectGMTCoord(s.xe);
if("yb"==name) return new ObjectGMTCoord(s.yb);
if("ye"==name) return new ObjectGMTCoord(s.ye);
return 0;
}
// GMTProjection
typedef ObjectGMTClass<struct gmt_projection> ObjectGMTProjection;
template<> inline ObjectBase* ObjectGMTProjection::OGet(const std::string& name) const
{
if("xb"==name) return new ObjectGMTCoord(s.region.xb);
if("xe"==name) return new ObjectGMTCoord(s.region.xe);
if("yb"==name) return new ObjectGMTCoord(s.region.yb);
if("ye"==name) return new ObjectGMTCoord(s.region.ye);
if("width"==name) return new ObjectReal(s.rwidth);
if("height"==name) return new ObjectReal(s.rheight);
if("region"==name) return new ObjectGMTRegion(s.region);
return 0;
}
// GMTColor
typedef ObjectGMTClass<struct gmt_color> ObjectGMTColor;
template<> inline ObjectBase* ObjectGMTColor::OGet(const std::string& name) const
{
if("gray"==name || "grey"==name) return new ObjectReal(s.Gray());
if("r"==name || "red"==name) return new ObjectReal(s.R());
if("g"==name || "green"==name) return new ObjectReal(s.G());
if("b"==name || "blue"==name) return new ObjectReal(s.B());
if("h"==name || "hue"==name) return new ObjectReal(s.H());
if("s"==name || "saturation"==name) return new ObjectReal(s.S());
if("v"==name || "value"==name) return new ObjectReal(s.V());
if("c"==name || "cyan"==name) return new ObjectReal(s.C());
if("m"==name || "magenta"==name) return new ObjectReal(s.M());
if("y"==name || "yellow"==name) return new ObjectReal(s.Y());
if("k"==name || "black"==name) return new ObjectReal(s.K());
if("t"==name || "transparency"==name) return new ObjectReal(s.transparency);
return 0;
}
// GMTDash
typedef ObjectGMTClass<struct gmt_dash> ObjectGMTDash;
// GMTPen
typedef ObjectGMTClass<struct gmt_pen> ObjectGMTPen;
template<> inline ObjectBase* ObjectGMTPen::OGet(const std::string& name) const
{
if("w"==name || "width"==name) return new ObjectReal(s.width);
if("c"==name || "color"==name) return new ObjectGMTColor(s.color);
if("d"==name || "dash"==name) return new ObjectGMTDash(s.dash);
return 0;
}
// GMTFont
typedef ObjectGMTClass<struct gmt_font> ObjectGMTFont;
template<> inline ObjectBase* ObjectGMTFont::OGet(const std::string& name) const
{
if("s"==name || "size"==name) return new ObjectReal(s.size);
if("f"==name || "family"==name)return new ObjectString(s.family);
if("c"==name || "color"==name) return new ObjectGMTColor(s.color);
return 0;
}
// GMTLayer
typedef ObjectGMTClass<struct gmt_layer> ObjectGMTLayer;
template<> inline ObjectBase* ObjectGMTLayer::OGet(const std::string& name) const
{
if("proj"==name || "projection"==name) return new ObjectGMTProjection(s.proj);
if("w"==name || "width"==name) return new ObjectReal(s.proj.rwidth);
if("h"==name || "height"==name) return new ObjectReal(s.proj.rheight);
if("shiftx"==name) return new ObjectReal(s.shiftx);
if("shifty"==name) return new ObjectReal(s.shifty);
return 0;
}
template<> const int8_t* ObjectGMTLayer::Blob(size_t* size) const;
template<> void ObjectGMTLayer::DeallocBlob(const int8_t* ptr) const;
#endif

297
modules/gmt/modgmt_param.h

@ -0,0 +1,297 @@
#ifndef MODGMT_PARAM_H
#define MODGMT_PARAM_H
#include "modgmt_strcomp.h"
#include "common.h"
// Common ancestor for all parameters
template<class Converter, bool Optional>
class Parameter
{
using ValueType=typename Converter::ValueType;
std::string name; // Used for error reporting
bool initialised;
Converter conv;
ValueType val;
template<bool hasDefault, class Conv=Converter>
struct Adapter;
template<class Conv>
struct Adapter<true,Conv> {static const typename Conv::ValueType& Val(const Conv& cnv) {return cnv.Default();}};
template<class Conv>
struct Adapter<false,Conv> {static typename Conv::ValueType Val(const Conv& cnv) {return typename Conv::ValueType();}};
template<class C>
struct CheckDefault
{
private:
static void detect(...);
template<class T> static decltype(std::declval<T>().Default()) detect(T);
public:
static constexpr bool value=std::is_same<ValueType, typename std::decay<decltype(detect(std::declval<C>()))>::type>::value;
};
protected:
Parameter(Parameter&&) = delete;
Parameter(const Parameter&) = delete;
Parameter() = delete;
template<class... Args>
Parameter(std::string&& str, Args... args): name(std::move(str)), initialised(false), conv(args...)
{
val=Adapter<CheckDefault<Converter>::value>::Val(conv);
}
template<class... Args>
Parameter(const std::string& str, Args... args): name(str), initialised(false), conv(args...)
{
val=Adapter<CheckDefault<Converter>::value>::Val(conv);
}
void SetState(bool newini) {initialised=newini;}
public:
bool Init(const ObjectBase* p, std::string& err)
{
std::string cerr;
bool res=true;
ValueType tval=conv.Convert(p,&res,cerr);
SetState(res);
if(res) val=std::move(tval);
else err=std::move(cerr);
return res;
}
bool Initialised() const {return initialised;}
bool Exist() const {return initialised;}
const std::string& Name() const {return name;}
constexpr bool isOptional() const {return Optional;}
const ValueType& Value() {return val;}
const ValueType* operator->() const {return &val;}
operator const ValueType&() {return val;}
void Reset() {initialised=false; val=Adapter<CheckDefault<Converter>::value>::Val(conv);}
static constexpr bool optional=Optional;
using ConverterType=Converter;
};
// Class for parameter which must be in named pair
template <class Converter, bool O>
class NamedParameter: public TemplateComparator, public Parameter<Converter,O>
{
public:
using AcceptableObject=void;
template<class... Args>
NamedParameter(const std::string& t, Args... args):TemplateComparator(t),Parameter<Converter,O>(Template2Name(),args...) {}
};
// Class for parameter which can be in named pair or as object of type Object
template <class Converter, bool O, class Object>
class NamedFreeParameter: public NamedParameter<Converter,O>
{
public:
using AcceptableObject=Object;
template<class... Args>
NamedFreeParameter(Args... args):NamedParameter<Converter,O>(args...) {}
};
// Class for parameter which can be in some positions in list
template <class Converter, bool O>
class PositionalParameter: public Parameter<Converter,O>
{
public:
template<class... Args>
PositionalParameter(Args... args):Parameter<Converter,O>(args...) {}
};
// Base class for ParseNamedParameters and ParsePositionalParameters
class ParseParameters
{
protected:
std::string err;
ParseParameters():err() {}
ParseParameters(const ParseParameters&) = delete;
ParseParameters(ParseParameters&&) = delete;
public:
std::string Error() const {return err;}
bool Ok() const {return err.empty();}
operator bool() const {return Ok();}
};
// Parsing positional parameters
class ParsePositionalParameters: public ParseParameters
{
ParsePositionalParameters() = delete;
ParsePositionalParameters(const ParsePositionalParameters&) = delete;
ParsePositionalParameters(ParsePositionalParameters&&) = delete;
// Main parsing function
template <class Par, class... Args>
void Parse(ObjectList::IndexType i, ObjectList::IndexType max, const ObjectList* ol, Par& param, Args&... args)
{
// Check types of arguments
static_assert(std::is_same<PositionalParameter<typename Par::ConverterType, Par::optional>,Par>::value,"ParsePositionalParameters argument(s) must be PositionalParameter");
// Check if parameter already initialised. This is code error.
if(param.Initialised())
{
err="Parameter "+param.Name()+" already initialised. This is code error.";
return;
}
// List is ended
if(i>=max)
{
// Parameter is optional, skip it
if(Par::optional) Parse(i,max,ol,args...);
// Parameter is required, this is an error
else err="Parameter "+param.Name()+" is required, but can't be setted because list is ended";
}
// Initialise from some list element
else
{
bool res=param.Init(ol->At(i),err);
// All Ok, continue to next element in list
if(res) Parse(i+1,max,ol,args...);
else
{
// All Ok, optional parameter may be absent, try to initialise next parameter by same list element
if(Par::optional) Parse(i,max,ol,args...);
// Error, required parameter not initialised
else err="Can't initialise parameter "+param.Name()+" from list element number "+ToString(i)+": "+err;
}
}
}
// Bottom of recursion
void Parse(ObjectList::IndexType i, ObjectList::IndexType max, const ObjectList* ol) {if(i<max) err="There are excess elements in list";}
public:
template <class Converter, bool optional, class... Args>
ParsePositionalParameters(const ObjectList* ol, PositionalParameter<Converter,optional>& p1, Args&... args) {Parse(0,ol->Size(),ol,p1,args...);}
template <class Converter, bool optional, class... Args>
ParsePositionalParameters(const ObjectList* ol, ObjectList::IndexType min, ObjectList::IndexType max, PositionalParameter<Converter,optional>& p1, Args&... args) {Parse(min,std::min(max,ol->Size()),ol,p1,args...);}
};
// Parsing named parameters
class ParseNamedParameters: public ParseParameters
{
ParseNamedParameters() = delete;
ParseNamedParameters(const ParseNamedParameters&) = delete;
ParseNamedParameters(ParseNamedParameters&&) = delete;
// Parsing function for elements without names
// ob - object from which we initialise parameter param.
// init - is ob was already used for initialise something.
// args - other parameters
// Function try to initialise parameter param, if param accepted real type of ob for initialisation, else function try to initialise next parameter in args.
// Function return error if initialisation of parameter failed or if two parameters can be initialised from ob.
template <class Par, class... Args>
bool ParseSingle(const ObjectBase* ob, bool init, Par& param, Args&... args)
{
// Check types of arguments
static_assert(std::is_same<NamedParameter<typename Par::ConverterType, Par::optional>,Par>::value || std::is_same<NamedFreeParameter<typename Par::ConverterType, Par::optional, typename Par::AcceptableObject>,Par>::value,"ParseNamedParameters argument(s) must be NamedParameter or NamedFreeParameter");
OBType<typename Par::AcceptableObject> o(ob);
if(o && init)
{
err="Object of type "+ob->Type()+" used for initialisation of two parameters. This is code error.";
return false;
}
if(!o) return ParseSingle(ob,init,args...); // Type mismatch, goto next parameter
if(param.Initialised())
{
err="Parameter "+param.Name()+" can't be initialised from object of type "+ob->Type()+" because it already initialised.";
return false;
}
std::string ierr;
if(!param.Init(ob,ierr))
{
err="Parameter "+param.Name()+" can't be initialised from object of type "+ob->Type()+": "+ierr;
return false;
}
return ParseSingle(ob,true,args...);
}
// Bottom of recursion
bool ParseSingle(const ObjectBase* ob, bool init) const {return true;}
// Parsing function for elements in pairs
// op - pair from which we initialise parameter param.
// pname - name of parameter already initialised from op (or empty string).
// args - other parameters
// Function try to initialise parameter param, if pair name corresponding to param template, else function try to initialise next parameter in args.
// Function return error if initialisation of parameter failed or if two parameters can be initialised from op.
template <class Par, class... Args>
bool ParsePair(const ObjectPair* op, std::string& pname, Par& param, Args&... args)
{
// Check types of arguments
static_assert(std::is_same<NamedParameter<typename Par::ConverterType, Par::optional>,Par>::value || std::is_same<NamedFreeParameter<typename Par::ConverterType, Par::optional, typename Par::AcceptableObject>,Par>::value,"ParseNamedParameters argument(s) must be NamedParameter or NamedFreeParameter");
bool cmp=param.Compare(op->Name());
if(cmp && !pname.empty())
{
err="Element "+op->Name()+" can be used for initialisation of two parameters: "+pname+" and "+param.Name();
return false;
}
if(!cmp) return ParsePair(op,pname,args...); // Name mismatch, goto next parameter
pname=param.Name();
if(param.Initialised())
{
err="Parameter "+param.Name()+" can't be initialised from element "+op->Name()+" because it already initialised.";
return false;
}
std::string ierr;
if(!param.Init(op->Value(),ierr))
{
err="Parameter "+param.Name()+" can't be initialised from element "+op->Name()+": "+ierr;
return false;
}
return ParsePair(op,pname,args...);
}
// Bottom of recursion
bool ParsePair(const ObjectPair* op, std::string& pname) const {return true;}
template <class Par, class... Args>
void CheckRequired(const Par& param, Args&... args)
{
if((Par::optional || param.Initialised())) CheckRequired(args...);
else err="Parameter "+param.Name()+" is required, but not initialised";
}
// Bottom of recursion
void CheckRequired() const {}
public:
template <class... Args>
ParseNamedParameters(const ObjectList* ol, Args&... args)
{
// Initialisation
std::string pname;
for(ObjectList::const_iterator i=ol->begin();i!=ol->end();++i)
{
pname.erase();
OBType<ObjectPair> p(*i);
if( !(p?ParsePair(p,pname,args...):ParseSingle(*i,false,args...)) ) break;
}
if(!Ok()) return; // Error on initialisation phase
// Check if all required parameters are initialised
CheckRequired(args...);
}
};
template<class Converter>
using ONPar=NamedParameter<Converter,true>;
template<class Converter>
using RNPar=NamedParameter<Converter,false>;
template<class Converter, class Object>
using ONFPar=NamedFreeParameter<Converter,true,Object>;
template<class Converter, class Object>
using RNFPar=NamedFreeParameter<Converter,false,Object>;
template<class Converter>
using OPosPar=PositionalParameter<Converter,true>;
template<class Converter>
using RPosPar=PositionalParameter<Converter,false>;
#endif

139
modules/gmt/modgmt_strcomp.cpp

@ -0,0 +1,139 @@
#include "modgmt_strcomp.h"
TemplateComparator::Block::pBlock TemplateComparator::Parse()
{
size_t e=0;
struct Block* blk;
struct Block* root;
auto c=s[e];
bool init,next;
// First symbol
if('['==c || '('==c) blk=new Block(('['==c)?Block::OPTIONAL:Block::VARIANTS);
else blk=new Block(e,e+1);
root=blk;
init=root->type!=Block::TEXT;
next=!init;
e++;
while(e<s.length())
{
c=s[e];
switch(c)
{
case '[':
case '(':
{
(next?blk->next:blk->child).reset(new Block(('['==c)?Block::OPTIONAL:Block::VARIANTS,(next?blk->parent:blk)));
blk=(next?blk->next:blk->child).get();
init=true;
next=false;
break;
}
case ']':
case ')':
{
blk=const_cast<struct Block*>(blk->parent);
init=true;
next=true;
break;
}
case '|': {blk->next.reset(new Block(Block::DELIM,blk->parent)); blk=blk->next.get(); init=next=true; break;}
default:
{
if(init)
{
(next?blk->next:blk->child).reset(new Block(e,e+1,(next?blk->parent:blk)));
blk=(next?blk->next:blk->child).get();
}
else blk->e=e+1;
init=false;
next=true;
}
}
e++;
}
return Block::pBlock(root);
}
void TemplateComparator::InitCursors(const struct TemplateComparator::Block* blk)
{
switch(blk->type)
{
case Block::TEXT: {cursors.insert({blk,0}); break;}
case Block::OPTIONAL:
{
auto b=blk;
InitCursors(b->child.get());
while(true)
{
if(!b->next) break;
b=b->next.get();
InitCursors(b);
if(b->type!=Block::OPTIONAL) break;
}
break;
}
case Block::VARIANTS:
{
auto b=blk->child.get();
while(true)
{
InitCursors(b);
while(b->type!=Block::DELIM)
{
if(!b->next) break;
b=b->next.get();
}
if(!b->next) break;
b=b->next.get();
}
}
default: {}
}
}
bool TemplateComparator::CmpSmb(const char c)
{
pCursor p=cursors.cbegin();
bool res=false;
// Compare symbol with all cursors
while(p!=cursors.end())
{
if(p->compare(s,c)) {res=true; p++;}
else p=cursors.erase(p);
}
Cursors old(std::move(cursors));
for(const auto& p:old)
{
auto blk=p.block;
// Increment cursors
if(blk->b+p.offset+1<blk->e) cursors.insert({blk,p.offset+1}); // Advance in current block
else // Move to next block
{
if(!blk->next || blk->next->type==Block::DELIM) // End of chain, must go up
{
while(blk->parent!=nullptr)
{
blk=blk->parent;
if(!blk->next || blk->next->type==Block::DELIM) continue;
InitCursors(blk->next.get());
}
}
else InitCursors(blk->next.get()); // Next block in chain
}
// Try to leave optional blocks
blk=p.block;
while(blk->parent!=nullptr)
{
blk=blk->parent;
if(blk->type!=Block::OPTIONAL || !blk->next || blk->next->type==Block::DELIM) continue;
InitCursors(blk->next.get());
}
}
return res;
}

98
modules/gmt/modgmt_strcomp.h

@ -0,0 +1,98 @@
#ifndef MODGMT_STRCOMP_H
#define MODGMT_STRCOMP_H
#include <memory>
#include <set>
#include <string>
// Compare string with template
class TemplateComparator
{
TemplateComparator() = delete;
TemplateComparator(const TemplateComparator&) = delete;
TemplateComparator(TemplateComparator&&) = delete;
struct Block
{
using pBlock=std::unique_ptr<struct Block>;
enum Type {NOTDEF,TEXT,OPTIONAL,VARIANTS,DELIM};
size_t b,e;
Type type;
const struct Block* parent;
pBlock next,child;
Block() = delete;
Block(const struct Block&) = delete;
Block(struct Block&&) = delete;
Block(Type t, const struct Block* p=nullptr):b(0),e(0),type(t),parent(p),next(nullptr),child(nullptr) {}
Block(size_t bb, size_t ee, const struct Block* p=nullptr):b(bb),e(ee),type(TEXT),parent(p),next(nullptr),child(nullptr) {}
};
struct Cursor
{
const struct Block* block;
size_t offset;
bool operator <(const struct Cursor& c) const {return block<c.block;}
bool compare(const std::string& str, const char c) const {return c==str[block->b+offset];}
Cursor(const struct Block* b, size_t o):block(b),offset(o) {}
};
using Cursors=std::set<struct Cursor>;
using pCursor=std::set<struct Cursor>::const_iterator;
Block::pBlock Parse();
void InitCursors(const struct Block* blk);
bool CmpSmb(const char c);
Block::pBlock root;
Cursors cursors;
const std::string s;
void Reset()
{
cursors.clear();
InitCursors(root.get());
}
public:
TemplateComparator(const char* str):s(str) {root=Parse();}
TemplateComparator(const std::string& str):s(str) {root=Parse();}
TemplateComparator(std::string&& str):s(std::move(str)) {root=Parse();}
bool Compare(const std::string& str)
{
Reset();
for(size_t pos=0; pos<str.length(); ++pos) if(!CmpSmb(str[pos])) return false;
return true;
}
std::string Template2Name() const
{
const struct Block* b=root.get();
std::string res;
while(true)
{
// Processing current block
switch(b->type)
{
case(Block::TEXT): {res+=s.substr(b->b,b->e-b->b); break;}
case(Block::OPTIONAL):
case(Block::VARIANTS): {b=b->child.get(); goto next;}
case(Block::DELIM): {b=b->parent; break;}
default: {}
}
// Go to next block
while(true)
{
if(nullptr!=b->next) {b=b->next.get(); goto next;}
if(nullptr!=b->parent) b=b->parent;
else break;
}
if(nullptr==b->next && nullptr==b->parent) break;
next: ;
}
return res;
}
};
#endif

900
modules/gmt/modgmt_structs.h

@ -0,0 +1,900 @@
#ifndef MODGMT_STRUCT_H
#define MODGMT_STRUCT_H
#include <cinttypes>
#include <cmath>
#include <map>
#include <vector>
#include "common.h"
#include "modgmt_colornames.h"
#include "modgmt_strcomp.h"
// Centimeters to GMT points scale factor
// 1 inch = 72 pt = 2.54 cm --> 1 cm = 72/2.54 pt
// GMT's own scaling factor is 0.06
inline static double cm2GMT(double cm)
{
static const double scale=(72.0/2.54)/0.06;
return cm*scale;
}
// Helper template for checking double numbers acquired from Source with some Policies.
// Main definition, never instantiated
template<class Source, class... Policies>
class GetDouble;
// Recursive definition
template<class Source, class Policy, class... Policies>
class GetDouble<Source, Policy, Policies...>: public GetDouble<Source, Policies...>
{
Policy p;
public:
GetDouble(GetDouble&&) = delete;
GetDouble(GetDouble&) = delete;
template<class... Args>
GetDouble(Args... args):GetDouble<Source,Policies...>(args...) {};
double operator()(bool* suc, std::string& err) const {return p(GetDouble<Source,Policies...>::operator()(suc,err),suc,err);}
};
// Bottom of recursion
template<class Source>
class GetDouble<Source>: public Source
{
public:
GetDouble(GetDouble&&) = delete;
GetDouble(GetDouble&) = delete;
GetDouble() = delete;
template<class... Args>
GetDouble(Args... args):Source(args...) {};
double operator()(bool* suc, std::string& err) const {return Source::operator()(suc, err);}
};
// We use rational representation of floating point number, because double type values not allowed as template parameter
// Policy to check if value is greater or equal num/denum
template<int32_t num, uint32_t denum=1, bool equal=true>
class PMin
{
static bool Compare(double d1, double d2) {return equal?(d1<=d2):(d1<d2);}
public:
double operator()(double v, bool* suc, std::string& err) const
{
if(Compare(v,static_cast<double>(num)/denum))
{
*suc=false;
if(err.empty()) err="Value "+ToString(v)+" must be greater "+(equal?"or equal ":"")+"then "+ToString(num)+((denum==1)?"":("/"+ToString(denum)));
}
return v;
}
};
// Policy to check if value is lesser or equal num/denum
template<int32_t num, uint32_t denum=1, bool equal=true>
class PMax
{
static bool Compare(double d1, double d2) {return equal?(d1>=d2):(d1>d2);}
public:
double operator()(double v, bool* suc, std::string& err) const
{
if(Compare(v,static_cast<double>(num)/denum))
{
*suc=false;
if(err.empty()) err="Value "+ToString(v)+" must be lesser "+(equal?"or equal ":"")+"then "+ToString(num)+((denum==1)?"":("/"+ToString(denum)));
}
return v;
}
};
// Get double value from string or number
class SourceValue
{
double d;
std::string err;
public:
SourceValue(const std::string& s) {if(!str2double(s,&d)) err="Can't convert string \""+s+"\" to double value";}
SourceValue(double s):d(s) {}
double operator()(bool* suc, std::string& ierr) const
{
if(!err.empty()) {*suc=false; ierr=err;}
return d;
}
};
// Helper types for colors
typedef GetDouble<SourceValue,PMin<0>,PMax<255> > Value2RGB;
typedef GetDouble<SourceValue,PMin<0>,PMax<360> > Value2Hue;
typedef GetDouble<SourceValue,PMin<0>,PMax< 1 > > Value2SV;
typedef GetDouble<SourceValue,PMin<0>,PMax<100> > Value2CMYK;
typedef GetDouble<SourceValue,PMin<0>,PMax<100> > Value2Transp;
// Helper type for pens and dashes
typedef GetDouble<SourceValue,PMin<0> > Value2Width;
class gmt_struct {}; // Base type for all gmt structures
// Coordinate
struct gmt_coord: public gmt_struct
{
bool isdeg;
union
{
double r;
struct
{
bool sign;
uint16_t d;
uint8_t m;
double s;
};
};
std::string Value() const
{
if(!isdeg) return ToString(r);
else return (sign?"":"-")+ToString(d)+":"+ToString(m)+(s==0?"":":"+ToString(s));
}
operator double() const
{
if(isdeg) return (d+(m+s/60.0)/60.0)*(sign?1:-1);
else return r;
}
bool Convert(const std::string& str, std::string& err)
{
WordList wl;
WordList::const_iterator cw;
wl=Split(str,":",true);
if(1==wl.size()) // No dd:mm
{
isdeg=false;
bool res=str2double(str,&r);
if(!res) err="Can't convert string "+str+" to double value";
return res;
}
if(0==wl.size() || wl.size()>3) {err="String "+str+" have incorrect format for geographic coordinate, must be ddd[:mm[:ss[.fff]]]"; return false;}
isdeg=true;
int64_t res;
// degrees
cw=wl.begin();
if(!str2int(*cw,&res)) {err="Can't convert "+*cw+" to integer"; return false;}
if(res>360 || res<-360) res%=360;
sign=(std::string::npos==cw->find('-'));
d=static_cast<uint16_t>(sign?res:-res);
// minutes
cw++;
if(!str2int(*cw,&res)) {err="Can't convert "+*cw+" to integer"; return false;}
if(res<0 || res>=60) {err="Minutes must be not lesser then 0 and lesser then 60"; return false;}
m=static_cast<uint8_t>(res);
s=0;
// seconds
cw++;
if(wl.end()==cw) return true; // No seconds
if(!str2double(*cw,&s) ) {err="Can't convert "+*cw+" to double"; return false;}
if(s<0.0 || s>=60.0) {err="Seconds must be not lesser then 0 and lesser then 60"; return false;}
return true;
}
bool Convert(double num, std::string& err)
{
isdeg=false;
r=num;
return true;
}
};
// Region
struct gmt_region: public gmt_struct
{
enum Type {NORMAL,BBOX,GLOBAL360,GLOBAL180};
Type type;
struct gmt_coord xb,xe,yb,ye;
std::string Value() const
{
switch(type)
{
case(NORMAL): return std::string("-R")+xb.Value()+"/"+xe.Value()+"/"+yb.Value()+"/"+ye.Value();
case(BBOX): return std::string("-R")+xb.Value()+"/"+yb.Value()+"/"+xe.Value()+"/"+ye.Value()+"r";
case(GLOBAL360): return "-Rg";
case(GLOBAL180): return "-Rd";
}
return "";
}
// Only "global", "global180" and "global360"
// TODO: add parsing of "-R" strings
bool Convert(const std::string& istr, std::string& err)
{
std::string str=istr;
tolower(str);
if("global180"==str)
{
type=GLOBAL180;
xb.Convert(-180.0,err); xe.Convert(180.0,err);
yb.Convert(-90.0,err); ye.Convert(90.0,err);
return true;
}
if("global360"==str || "global"==str)
{
type=GLOBAL360;
xb.Convert(0.0,err); xe.Convert(360.0,err);
yb.Convert(-90.0,err); ye.Convert(90.0,err);
return true;
}
err="Can't convert "+istr+" to region.";
return false;
}
// Make by coordinates
bool Convert(struct gmt_coord ixb, struct gmt_coord ixe, struct gmt_coord iyb, struct gmt_coord iye, bool isbbox=false)
{
type=isbbox?BBOX:NORMAL;
xb=ixb; yb=iyb; xe=ixe; ye=iye;
return true;
}
};
// Projection
struct gmt_projection: public gmt_struct
{
// OBLIQMERCATOR types
enum class OType {NOTDEF,A,B,C};
// No UTM (-Ju)
enum projection {NOTDEF,XY,CYL_EQU,MERCATOR,TRANSMERCATOR,OBLIQMERCATOR,CASSINI,CYL_EQA,MILLER,CYL_STERE};
// Real size in cm of drawing area
double rwidth,rheight;
struct gmt_region region;
projection proj;
double width; // parameter of projection
static const double default_width;
union // other projection parameters
{
// XY
struct {double height;} x;
// Cylindrical projections
// CYL_EQU (Cylindrical Equidistant -Jq)
struct {struct gmt_coord stpar,cmer;} q;
// MERCATOR (-Jm)
struct {struct gmt_coord stpar,cmer;} m;
// TRANSMERCATOR (-Jt)
struct {struct gmt_coord cmer,orlat; double scale;} t;
// OBLIQMERCATOR (-Jo)
struct
{
OType type;
struct gmt_coord clon,clat;
union
{
struct gmt_coord azimuth; // A
struct {struct gmt_coord eqlon,eqlat;}; // B
struct {struct gmt_coord polelon,polelat;}; // C
};
} o;
// CASSINI (-Jc)
struct {struct gmt_coord clon,clat;} c;
// CYL_EQA (Cylindrical equal-area -Jy)
struct {struct gmt_coord stpar,cmer;} y;
// MILLER (-Jj)
struct {struct gmt_coord cmer;} j;
// CYL_STERE (Cylindrical stereographic -Jcyl_stere)
struct {struct gmt_coord stpar,cmer;} cyl_stere;
};
std::string Value() const
{
std::string ret;
switch(proj)
{
case(XY): {ret="-JX"+ToString(width)+"c/"+ToString(x.height)+"c"; break;}
case(CYL_EQU): {ret="-JQ"+q.cmer.Value()+"/"+q.stpar.Value()+"/"+ToString(width)+"c"; break;}
case(MERCATOR): {ret="-JM"+m.cmer.Value()+"/"+m.stpar.Value()+"/"+ToString(width)+"c"; break;}
case(TRANSMERCATOR): {ret="-JT"+t.cmer.Value()+"/"+t.orlat.Value()+"/"+ToString(width)+"c --PROJ_SCALE_FACTOR="+ToString(t.scale); break;}
case(OBLIQMERCATOR):
{
switch(o.type)
{
case(OType::A): {ret="-JOa"+o.clon.Value()+"/"+o.clat.Value()+"/"+o.azimuth.Value()+"/"+ToString(width)+"c"; break;}
case(OType::B): {ret="-JOb"+o.clon.Value()+"/"+o.clat.Value()+"/"+o.eqlon.Value()+"/"+o.eqlat.Value()+"/"+ToString(width)+"c"; break;}
case(OType::C): {ret="-JOb"+o.clon.Value()+"/"+o.clat.Value()+"/"+o.polelon.Value()+"/"+o.polelat.Value()+"/"+ToString(width)+"c"; break;}
default: return "";
}
break;
}
case(CASSINI): {ret="-JC"+c.clon.Value()+"/"+c.clat.Value()+"/"+ToString(width)+"c"; break;}
case(CYL_EQA): {ret="-JY"+y.cmer.Value()+"/"+y.stpar.Value()+"/"+ToString(width)+"c"; break;}
case(MILLER): {ret="-JJ"+j.cmer.Value()+"/"+ToString(width)+"c"; break;}
case(CYL_STERE): {ret="-JCyl_stere/"+cyl_stere.stpar.Value()+"/"+cyl_stere.cmer.Value()+"/"+ToString(width)+"c"; break;}
default: return "";
}
ret+=" "+region.Value();
return ret;
}
bool SetType(const std::string& s, std::string& err)
{
proj=NOTDEF;
std::string str=s;
tolower(str);
// Handle one symbol cases
if(str.size()==1) switch(str[0])
{
case('c'): {proj=CASSINI; return true;}
case('j'): {proj=MILLER; return true;}
case('m'): {proj=MERCATOR; return true;}
case('o'): {proj=OBLIQMERCATOR; return true;}
case('q'): {proj=CYL_EQU; return true;}
case('t'): {proj=TRANSMERCATOR; return true;}
case('x'): {proj=XY; return true;}
case('y'): {proj=CYL_EQA; return true;}
default: break;
}
// Handle long GMT names
if("cyl_stere"==str) {proj=CYL_STERE; return true;}
// Handle common names
std::string t1;
for(const auto& m: projnames)
{
TemplateComparator cmp(m.first);
bool res=cmp.Compare(str);
if(!res) continue;
if(NOTDEF!=proj) // Ambiguos string
{
err="Ambiguous projection definition: did you mean \""+t1+"\" or \""+cmp.Template2Name()+"\"?";
proj=NOTDEF;
return false;
}
t1=cmp.Template2Name();
proj=m.second;
}
if(NOTDEF!=proj) return true; // All Ok
err="Unknown projection: "+s;
return false;
}
static void FillProjNames();
private:
static std::map<std::string,projection> projnames;
};
// Color
struct gmt_color: public gmt_struct
{
public:
enum ColorModel {RGB,GRAY,HSV,CMYK};
ColorModel model;
union
{
double gray; // 0-255
struct {double r,g,b;}; // 0-255
struct {double hue,saturation,value;}; // 0-360, 0-1, 0-1
struct {double cyan,magenta,yellow,black;}; // 0-100
};
double transparency;
std::string Value() const
{
std::string trans=(transparency!=0)?("@"+ToString(transparency)):"";
switch(model)
{
case(RGB): return ToString(r)+"/"+ToString(g)+"/"+ToString(b)+trans;
case(GRAY): return ToString(gray)+trans;
case(HSV): return ToString(hue)+"-"+ToString(saturation)+"-"+ToString(value)+trans;
case(CMYK): return ToString(cyan)+"/"+ToString(magenta)+"/"+ToString(yellow)+"/"+ToString(black)+trans;
}
return "";
}
double Gray() const
{
switch(model)
{
case(RGB): return RGB2Gray().gray;
case(GRAY): return gray;
case(HSV): return HSV2Gray().gray;
case(CMYK): return CMYK2Gray().gray;
}
return 0;
}
void ToGray()
{
switch(model)
{
case(RGB): gray=RGB2Gray().gray; break;
case(GRAY): return;
case(HSV): gray=HSV2Gray().gray; break;
case(CMYK): gray=CMYK2Gray().gray; break;
}
model=GRAY;
}
double R() const
{
switch(model)
{
case(RGB): return r;
case(GRAY): return Gray2RGB().r;
case(HSV): return HSV2RGB().r;
case(CMYK): return CMYK2RGB().r;
}
return 0;
}
double G() const
{
switch(model)
{
case(RGB): return g;
case(GRAY): return Gray2RGB().g;
case(HSV): return HSV2RGB().g;
case(CMYK): return CMYK2RGB().g;
}
return 0;
}
double B() const
{
switch(model)
{
case(RGB): return b;
case(GRAY): return Gray2RGB().b;
case(HSV): return HSV2RGB().b;
case(CMYK): return CMYK2RGB().b;
}
return 0;
}
void ToRGB()
{
gmt_color c;
switch(model)
{
case(RGB): return;
case(GRAY): c=Gray2RGB(); break;
case(HSV): c=HSV2RGB(); break;
case(CMYK): c=CMYK2RGB(); break;
}
model=RGB;
r=c.r; g=c.g; b=c.b;
}
double H() const
{
switch(model)
{
case(RGB): return RGB2HSV().hue;
case(GRAY): return Gray2HSV().hue;
case(HSV): return hue;
case(CMYK): return CMYK2HSV().hue;
}
return 0;// Own functions
}
double S() const
{
switch(model)
{
case(RGB): return RGB2HSV().saturation;
case(GRAY): return Gray2HSV().saturation;
case(HSV): return saturation;
case(CMYK): return CMYK2HSV().saturation;
}
return 0;
}
double V() const
{
switch(model)
{
case(RGB): return RGB2HSV().value;
case(GRAY): return Gray2HSV().value;
case(HSV): return value;
case(CMYK): return CMYK2HSV().value;
}
return 0;
}
void ToHSV()
{
gmt_color c;
switch(model)
{
case(RGB): c=RGB2HSV(); break;
case(GRAY): c=Gray2HSV(); break;
case(HSV): return;
case(CMYK): c=CMYK2HSV(); break;
}
model=HSV;
hue=c.hue; saturation=c.saturation; value=c.value;
}
double C() const
{
switch(model)
{
case(RGB): return RGB2CMYK().cyan;
case(GRAY): return Gray2CMYK().cyan;
case(HSV): return HSV2CMYK().cyan;
case(CMYK): return cyan;
}
return 0;
}
double M() const
{
switch(model)
{
case(RGB): return RGB2CMYK().magenta;
case(GRAY): return Gray2CMYK().magenta;
case(HSV): return HSV2CMYK().magenta;
case(CMYK): return magenta;
}
return 0;
}
double Y() const
{
switch(model)
{
case(RGB): return RGB2CMYK().yellow;
case(GRAY): return Gray2CMYK().yellow;
case(HSV): return HSV2CMYK().yellow;
case(CMYK): return yellow;
}
return 0;
}
double K() const
{
switch(model)
{
case(RGB): return RGB2CMYK().black;
case(GRAY): return Gray2CMYK().black;
case(HSV): return HSV2CMYK().black;
case(CMYK): return black;
}
return 0;
}
void ToCMYK()
{
gmt_color cc;
switch(model)
{
case(RGB): cc=RGB2CMYK(); break;
case(GRAY): cc=Gray2CMYK(); break;
case(HSV): cc=HSV2CMYK(); break;
case(CMYK): return;
}
model=CMYK;
cyan=cc.cyan; magenta=cc.magenta; yellow=cc.yellow; black=cc.black;
}
void ToModel(ColorModel newmodel)
{
switch(newmodel)
{
case(RGB): ToRGB(); return;
case(GRAY): ToGray(); return;
case(HSV): ToHSV(); return;
case(CMYK): ToCMYK(); return;
}
}
// Interpret one numeric argument as gray value
bool Convert(double gr, std::string& err)
{
Value2RGB g(gr);
bool suc=true;
model=GRAY;
transparency=0.0;
gray=g(&suc,err);
return suc;
}
bool Convert(const std::string& istr, std::string& err)
{
std::string cstr=istr;
tolower(cstr);
transparency=0.0;
// Transparency check
{
WordList wl=Split(cstr,"@");
if(1!=wl.size() && 2!=wl.size()) return false;
if(2==wl.size())
{
WordList::const_iterator ci=wl.begin();
bool suc=true;
cstr=*ci;
ci++;
Value2Transp t(*ci);
transparency=t(&suc,err);
if(!suc) return false; // Parse error
}
}
{
WordList wl_slash=Split(cstr,"/"),wl_hyphen=Split(cstr,"-");
WordList::size_type slash_size=wl_slash.size(),hyphen_size=wl_hyphen.size();
// Checks
if(slash_size>1 && hyphen_size>1) {err="Delimeter of color components must be \"/\" or \"-\", not both"; return false;}
if(2==slash_size || slash_size>4) {err="Number of color components must be 1 for GRAY, 3 for RGB or 4 for CMYK"; return false;}
if(2==hyphen_size || hyphen_size>3) {err="There is 3 color components in HSV model"; return false;}
// Gray or name
// TODO: Hex representation
if(1==slash_size && 1==hyphen_size)
{
if(SetByName(cstr)) return true; // Set color by name, all ok
Value2RGB g(cstr);
bool suc=true;
model=GRAY;
gray=g(&suc,err);
err="Can't understand string "+cstr+", this is nor color name, nor gray value (0-255)";
return suc;
}
// RGB
if(3==slash_size)
{
Value2RGB red(wl_slash.front()); wl_slash.pop_front();
Value2RGB green(wl_slash.front()); wl_slash.pop_front();
Value2RGB blue(wl_slash.front());
bool suc=true;
model=RGB;
r=red(&suc,err); if(!suc) return suc;
g=green(&suc,err);if(!suc) return suc;
b=blue(&suc,err);
return suc;
}
// HSV
if(3==hyphen_size)
{
Value2Hue h(wl_hyphen.front()); wl_hyphen.pop_front();
Value2SV s(wl_hyphen.front()); wl_hyphen.pop_front();
Value2SV v(wl_hyphen.front());
bool suc=true;
model=HSV;
hue=h(&suc,err); if(!suc) return suc;
saturation=s(&suc,err);if(!suc) return suc;
value=v(&suc,err);
return suc;
}
// CMYK
if(4==slash_size)
{
Value2CMYK c(wl_slash.front()); wl_slash.pop_front();
Value2CMYK m(wl_slash.front()); wl_slash.pop_front();
Value2CMYK y(wl_slash.front()); wl_slash.pop_front();
Value2CMYK k(wl_slash.front());
bool suc=true;
model=CMYK;
cyan=c(&suc,err); if(!suc) return suc;
magenta=m(&suc,err);if(!suc) return suc;
yellow=y(&suc,err); if(!suc) return suc;
black=k(&suc,err);
return suc;
}
}
return false;
}
bool SetByName(const std::string& name)
{
const struct colorname* cdata;
model=RGB;
cdata=ColorHash::in_colors_set(name.c_str(),name.length());
if(0==cdata) return false;
r=cdata->r;
g=cdata->g;
b=cdata->b;
return true;
}
// Transformation functions
private:
#include "modgmt_colortransform.h"
};
// Dash
struct gmt_dash: public gmt_struct
{
std::vector<double> dash;
double shift;
double width;
bool wisrel;
static const double default_width;
std::string Value() const
{
std::string ret;
if(dash.empty()) return ret;
for(auto i:dash) ret+=ToString((wisrel?width:1.0)*i/10.0)+"_";
ret.back()=':';
ret+=ToString(shift)+"c";
return ret;
}
operator bool() const {return !dash.empty();}
void Clear() {dash.clear(); shift=0.0; wisrel=false;}
bool Convert(const std::string& in, std::string& err, double w=0.0)
{
Clear();
shift=0;
if(0==in.size()) return true; // No dash
if(std::string::npos==in.find_first_not_of(".-")) // dot-dash form
{
width=(0==w)?default_width:w;
wisrel=true;
for(const auto& i:in)
{
if('.'==i) dash.push_back(1); // Dot
else dash.push_back(8); // Dash
dash.push_back(4); // Gap
}
return true;
}
std::string dstr=in;
// Determine shift
{
WordList wl=Split(in,":");
if(wl.size()>2) {err="Incorrect dash format"; return false;}
if(2==wl.size())
{
Value2Width s(wl.back());
bool suc=true;
shift=s(&suc,err);
if(!suc) return false; // Parse error
dstr=wl.front();
}
}
WordList wl=Split(dstr,"_");
for(const auto& i:wl)
{
Value2Width d(i);
bool suc=true;
dash.push_back(d(&suc,err));
if(!suc) return false; // Parse error
}
return true;
}
};
// Pen
struct gmt_pen: public gmt_struct
{
double width;
struct gmt_color color;
struct gmt_dash dash;
static const double default_width;
std::string Value() const {return ToString(width/10.0)+"c,"+color.Value()+(dash?(","+dash.Value()):"");}
// Interpret one numeric argument as width value
bool Convert(double in, std::string& err)
{
Value2Width w(in);
bool suc=true;
color.Convert(0,err); // Black
dash.Clear();
width=w(&suc,err);
return suc;
}
// Convert from string
bool Convert(const std::string& istr, std::string& err)
{
std::string str=istr;
WordList::const_iterator ci;
tolower(str);
WordList wl=Split(str,",",true);
// Defaults
width=default_width;
color.Convert(0,err); // Black
dash.Clear();
if(wl.size()>3) return false; // String is [width][,color][,dash]
ci=wl.begin();
if(wl.end()!=ci && 0!=ci->size())
{
Value2Width w(*ci);
bool suc=true;
width=w(&suc,err);
if(!suc) return false; // Parse error
}
if(wl.end()!=ci) ci++;
if(wl.end()!=ci && 0!=ci->size())
{
if(!color.Convert(*ci,err)) return false; // Parse error
}
if(wl.end()!=ci) ci++;
if(wl.end()!=ci && 0!=ci->size())
{
if(!dash.Convert(*ci,err,width)) return false; // Parse error
}
return true;
}
};
// Font
struct gmt_font: public gmt_struct
{
double size;
struct gmt_color color;
std::string family;
static std::string default_family;
static std::set<std::string> families;
static const double default_size;
std::string Value() const {return ToString(size)+"p,"+family+","+color.Value();}
// Interpret one numeric argument as size of default black font
bool Convert(double in, std::string& err)
{
Value2Width s(in);
bool suc=true;
color.Convert(0,err); // Black
size=s(&suc,err);
family=default_family;
return suc;
}
// Convert from string
bool Convert(const std::string& str, std::string& err)
{
WordList::const_iterator ci;
WordList wl=Split(str,",",true);
std::string fakeerr;
// Defaults
size=default_size;
family=default_family;
color.Convert(0,fakeerr); // Black
if(wl.size()>3) {err="String "+str+" is not font string"; return false;} // String is [size][,family][,color] or [family][,color]
ci=wl.begin();
if(wl.end()!=ci && 0!=ci->size())
{
Value2Width s(*ci);
bool suc=true;
{
std::string fake;
double newsize=s(&suc,fake);
if(suc) size=newsize;
}
if(!suc) // Parse error. check if argument is font name
{
if(0==families.count(*ci)) return false; // No, argument is not allowed font name
family=*ci;
if(wl.size()>2) {err="String "+str+" is not font string"; return false;} // If first word is font name, then words count is 1 or 2.
goto read_color;
}
}
if(wl.end()!=ci) ci++;
if(wl.end()!=ci && 0!=ci->size())
{
if(0==families.count(*ci)) {err="Unknown font family: "+(*ci); return false;} // Argument is not allowed font name
family=*ci;
}
read_color:
if(wl.end()!=ci) ci++;
if(wl.end()!=ci && 0!=ci->size())
{
if(!color.Convert(*ci,err)) return false; // Parse error
}
return true;
}
static bool FillFontNames();
};
// Layer
struct gmt_layer: public gmt_struct
{
double shiftx,shifty;
struct gmt_projection proj;
std::string creator;
std::shared_ptr<std::string> data;
std::string Value() const {return creator+(Shifted()?("("+ToString(shiftx)+"x"+ToString(shifty)+")"):"");}
bool Shifted() const {return shiftx!=0.0 || shifty!=0.0;}
std::string BeginShift() const {return (Shifted()?("V "+ToString(cm2GMT(shiftx))+" "+ToString(cm2GMT(shifty))+" T\n"):"");}
std::string EndShift() const {return (Shifted()?"U\n":"");}
};
#endif

43
src/CMakeLists.txt

@ -0,0 +1,43 @@
cmake_minimum_required(VERSION 2.8.5)
set(EXENAME makemap)
find_package(FLEX REQUIRED)
find_package(BISON REQUIRED)
find_library(dl dl)
find_library(pthread pthread)
if(NOT dl)
message(FATAL_ERROR "libdl not found!")
endif()
if(NOT pthread)
message(FATAL_ERROR "libpthread not found!")
endif()
file(GLOB srcs *.cpp)
BISON_TARGET(GParser parser/grammatical.y ${CMAKE_CURRENT_BINARY_DIR}/grammatical.cpp)
FLEX_TARGET(LScanner parser/lexical.l ${CMAKE_CURRENT_BINARY_DIR}/lexical.cpp COMPILE_FLAGS "--header-file=${CMAKE_CURRENT_BINARY_DIR}/lexical.h")
set_property(DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES lexical.h)
set(MODULES_SOURCES "")
set(MODULES_LIBS "")
foreach(moddir ${STATIC_MODULES})
include(${moddir}/ModuleSetup.cmake OPTIONAL)
file(GLOB modsrcs ${moddir}/*.cpp)
set(MODULES_SOURCES ${MODULES_SOURCES} ${modsrcs} ${MODULE_ADDITIONAL_SOURCES})
set(MODULES_LIBS ${MODULES_LIBS} ${MODULE_ADDITIONAL_LIBRARIES})
include_directories(${MODULE_ADDITIONAL_INCLUDES})
endforeach()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_executable(${EXENAME} ${srcs} ${FLEX_LScanner_OUTPUTS} ${BISON_GParser_OUTPUTS} ${MODULES_SOURCES})
# Workaround. BISON_TARGET makes header with extension .hpp, but we use extension .h
set_source_files_properties(init.cpp PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/grammatical.h)
add_custom_command(OUTPUT grammatical.h COMMAND cp grammatical.hpp grammatical.h DEPENDS grammatical.hpp WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(${EXENAME} ${linker_options} ${dl} ${pthread} ${MODULES_LIBS})
set_target_properties(${EXENAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)

8
src/Makefile

@ -5,8 +5,8 @@ CPPFLAGS=-std=gnu++11 -I../include
LIBSFLAGS=-ldl -lpthread $(MODLIBS) LIBSFLAGS=-ldl -lpthread $(MODLIBS)
WARNFLAGS=-Wall WARNFLAGS=-Wall
CFLAGS=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(CPPFLAGS) CFLAGS+=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(CPPFLAGS)
LDFLAGS=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(LIBSFLAGS) LDFLAGS+=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(LIBSFLAGS)
CC=g++ CC=g++
@ -31,11 +31,11 @@ $(DEPENDS): ../include/grammatical.h ../include/lexical.h
parser/grammatical.d: ../include/lexical.h parser/grammatical.d: ../include/lexical.h
parser/lexical.cpp: parser/lexical.l parser/lexical.cpp: parser/lexical.l
cd parser && flex lexical.l flex -o $@ --header-file=../include/lexical.h $<
[ -f ../include/lexical.h ] && touch ../include/lexical.h [ -f ../include/lexical.h ] && touch ../include/lexical.h
parser/grammatical.cpp: parser/grammatical.y parser/grammatical.cpp: parser/grammatical.y
cd parser && bison grammatical.y bison -o $@ --defines=../include/grammatical.h $<
[ -f ../include/grammatical.h ] && touch ../include/grammatical.h [ -f ../include/grammatical.h ] && touch ../include/grammatical.h
../include/grammatical.h: parser/grammatical.y parser/grammatical.cpp ../include/grammatical.h: parser/grammatical.y parser/grammatical.cpp

155
src/builtin.cpp

@ -1,155 +0,0 @@
#include <math.h>
#include "object.h"
ObjectBase* Arifm_Add(const ObjectList* input)
{
if(input->Size()!=2) return 0;
const ObjectBase *arg1=input->At(0),*arg2=input->At(1);
std::type_index t1(typeid(*arg1)),t2(typeid(*arg2));
std::type_index tr(typeid(ObjectReal)),ti(typeid(ObjectInt));
if( (t1!=tr && t1!=ti) || (t2!=tr && t2!=ti) ) return 0;
// Integer arifmetic
if(t1==ti && t2==ti) return new ObjectInt(dynamic_cast<const ObjectInt*>(arg1)->Value() + dynamic_cast<const ObjectInt*>(arg2)->Value());
// Real arifmetic
double r1,r2;
if(t1==tr) r1=dynamic_cast<const ObjectReal*>(arg1)->Value();
else r1=dynamic_cast<const ObjectInt*>(arg1)->Value();
if(t2==tr) r2=dynamic_cast<const ObjectReal*>(arg2)->Value();
else r2=dynamic_cast<const ObjectInt*>(arg2)->Value();
return new ObjectReal(r1+r2);
}
ObjectBase* Arifm_Sub(const ObjectList* input)
{
if(input->Size()!=2) return 0;
const ObjectBase *arg1=input->At(0),*arg2=input->At(1);
std::type_index t1(typeid(*arg1)),t2(typeid(*arg2));
std::type_index tr(typeid(ObjectReal)),ti(typeid(ObjectInt));
if( (t1!=tr && t1!=ti) || (t2!=tr && t2!=ti) ) return 0;
// Integer arifmetic
if(t1==ti && t2==ti) return new ObjectInt(dynamic_cast<const ObjectInt*>(arg1)->Value() - dynamic_cast<const ObjectInt*>(arg2)->Value());
// Real arifmetic
double r1,r2;
if(t1==tr) r1=dynamic_cast<const ObjectReal*>(arg1)->Value();
else r1=dynamic_cast<const ObjectInt*>(arg1)->Value();
if(t2==tr) r2=dynamic_cast<const ObjectReal*>(arg2)->Value();
else r2=dynamic_cast<const ObjectInt*>(arg2)->Value();
return new ObjectReal(r1-r2);
}
ObjectBase* Arifm_Mul(const ObjectList* input)
{
if(input->Size()!=2) return 0;
const ObjectBase *arg1=input->At(0),*arg2=input->At(1);
std::type_index t1(typeid(*arg1)),t2(typeid(*arg2));
std::type_index tr(typeid(ObjectReal)),ti(typeid(ObjectInt));
if( (t1!=tr && t1!=ti) || (t2!=tr && t2!=ti) ) return 0;
// Integer arifmetic
if(t1==ti && t2==ti) return new ObjectInt(dynamic_cast<const ObjectInt*>(arg1)->Value() * dynamic_cast<const ObjectInt*>(arg2)->Value());
// Real arifmetic
double r1,r2;
if(t1==tr) r1=dynamic_cast<const ObjectReal*>(arg1)->Value();
else r1=dynamic_cast<const ObjectInt*>(arg1)->Value();
if(t2==tr) r2=dynamic_cast<const ObjectReal*>(arg2)->Value();
else r2=dynamic_cast<const ObjectInt*>(arg2)->Value();
return new ObjectReal(r1*r2);
}
ObjectBase* Arifm_Div(const ObjectList* input)
{
if(input->Size()!=2) return 0;
const ObjectBase *arg1=input->At(0),*arg2=input->At(1);
std::type_index t1(typeid(*arg1)),t2(typeid(*arg2));
std::type_index tr(typeid(ObjectReal)),ti(typeid(ObjectInt));
if( (t1!=tr && t1!=ti) || (t2!=tr && t2!=ti) ) return 0;
// Integer arifmetic
if(t1==ti && t2==ti) return new ObjectInt(dynamic_cast<const ObjectInt*>(arg1)->Value() / dynamic_cast<const ObjectInt*>(arg2)->Value());
// Real arifmetic
double r1,r2;
if(t1==tr) r1=dynamic_cast<const ObjectReal*>(arg1)->Value();
else r1=dynamic_cast<const ObjectInt*>(arg1)->Value();
if(t2==tr) r2=dynamic_cast<const ObjectReal*>(arg2)->Value();
else r2=dynamic_cast<const ObjectInt*>(arg2)->Value();
return new ObjectReal(r1/r2);
}
ObjectBase* Arifm_Pow(const ObjectList* input)
{
if(input->Size()!=2) return 0;
const ObjectBase *arg1=input->At(0),*arg2=input->At(1);
std::type_index t1(typeid(*arg1)),t2(typeid(*arg2));
std::type_index tr(typeid(ObjectReal)),ti(typeid(ObjectInt));
if( (t1!=tr && t1!=ti) || (t2!=tr && t2!=ti) ) return 0;
// Only real arifmetic
double r1,r2;
if(t1==tr) r1=dynamic_cast<const ObjectReal*>(arg1)->Value();
else r1=dynamic_cast<const ObjectInt*>(arg1)->Value();
if(t2==tr) r2=dynamic_cast<const ObjectReal*>(arg2)->Value();
else r2=dynamic_cast<const ObjectInt*>(arg2)->Value();
return new ObjectReal(pow(r1,r2));
}
ObjectBase* Arifm_Neg(const ObjectList* input)
{
if(input->Size()!=1) return 0;
const ObjectBase *arg=input->At(0);
std::type_index t(typeid(*arg));
std::type_index tr(typeid(ObjectReal)),ti(typeid(ObjectInt));
// Integer arifmetic
if(t==ti) return new ObjectInt(-dynamic_cast<const ObjectInt*>(arg)->Value());
// Real arifmetic
if(t==tr) return new ObjectReal(-dynamic_cast<const ObjectReal*>(arg)->Value());
return 0;
}
ObjectBase* Arifm_Pos(const ObjectList* input)
{
if(input->Size()!=1) return 0;
const ObjectBase *arg=input->At(0);
std::type_index t(typeid(*arg));
std::type_index tr(typeid(ObjectReal)),ti(typeid(ObjectInt));
// Integer arifmetic
if(t==ti) return new ObjectInt(dynamic_cast<const ObjectInt*>(arg)->Value());
// Real arifmetic
if(t==tr) return new ObjectReal(dynamic_cast<const ObjectReal*>(arg)->Value());
return 0;
}

353
src/deptree.cpp

@ -1,36 +1,43 @@
#include <stack> #include <stack>
#include "deptree.h" #include "deptree.h"
int DepTree::CreateNodeFromVar(const std::string& var, DepTreeVars& vars, CallStack& callstack) // Create variable node
// v - variable name
// vars - definitions of variables
// dvars - variables which are already in tree
// callstack - path in tree used for detections of loops.
int DepTree::CreateNodeFromVar(const std::string& v, VarType& vars, DepTree::DepTreeVars& dvars, DepTree::CallStack& callstack)
{ {
COUT(DEBUG)<<"DepTree::CreateNodeFromVar "<<var<<std::endl; COUT(DEBUG)<<"DepTree::CreateNodeFromVar "<<v<<std::endl;
if(G_vars.count(var)==0) if(vars.count(v)==0)
{ {
COUT(ERROR)<<"Definition of variable "<<var<<" not found"<<std::endl; COUT(ERROR)<<"Definition of variable "<<v<<" not found"<<std::endl;
return 1; return 1;
} }
UsedType ids; UsedType ids=UsedVars(vars[v]);
vars[var]=this; dvars[v]=this;
G_vars[var]->UsedIdents(ids);
type=DepTree::VAR; type=DepTree::VAR;
name=var; name=v;
DepTreeVars::const_iterator d; DepTreeVars::const_iterator d;
int ret; int ret;
exe.splice(exe.begin(),vars[v]); // Move expression into node
callstack.insert(this); callstack.insert(this);
for(auto& i:ids) for(auto& i:ids)
{ {
d=vars.find(i); d=dvars.find(i);
if(d==vars.end()) if(d==dvars.end())
{ {
auto n=*childrens.insert(new DepTree).first; auto n=*childrens.insert(new DepTree).first;
n->parents.insert(this); n->parents.insert(this);
ret=n->CreateNodeFromVar(i,vars,callstack); ret=n->CreateNodeFromVar(i,vars,dvars,callstack);
if(ret!=0) if(ret!=0)
{ {
COUT(ERROR)<<" in definition of variable "<<name<<std::endl; const struct grammatic_location& loc=exe.back().Location();
COUT(ERROR)<<" in definition of variable "<<name<<" at line "<<loc.first_line<<", position "<<loc.first_column<<std::endl;
for(const auto& inc: loc.incstack) COUT(ERROR)<<" included from "<<inc.filename<<" at line "<<inc.line<<", position "<<inc.column<<std::endl;
return ret; return ret;
} }
} }
@ -50,41 +57,45 @@ int DepTree::CreateNodeFromVar(const std::string& var, DepTreeVars& vars, CallSt
return 0; return 0;
} }
// Creating PRINT or SAVE node
int DepTree::CreateNodeFromSP(DepTree::NodeType list, G_toType::size_type ind, DepTreeVars& vars) // list - type of node
// vars - definitions of variables
// exp - expression to print or save
// dvars - variables which are already in tree
int DepTree::CreateNodeFromSP(DepTree::NodeType list, VarType& vars, ExecExpr& exp, DepTreeVars& dvars)
{ {
if(list!=DepTree::SAVE && list!=DepTree::PRINT) if(list!=DepTree::SAVE && list!=DepTree::PRINT)
{ {
COUT(ERROR)<<"Internal error, incorrect NodeType in DepTree::CreateNodeFromSP()."<<std::endl; COUT(ERROR)<<"Internal error, incorrect NodeType in DepTree::CreateNodeFromSP()."<<std::endl;
return 2; return 2;
} }
COUT(DEBUG)<<"DepTree::CreateNodeFromSP "<<((list==DepTree::SAVE)?G_tosave:G_toprint)[ind]->Dump()<<std::endl;
std::set<std::string> ids; UsedType ids=UsedVars(exp);
CallStack callstack; CallStack callstack;
DepTreeVars::const_iterator d;
int ret;
type=list; type=list;
index=ind; exe.splice(exe.end(),exp); // Move expression into node
COUT(DEBUG)<<"DepTree::CreateNodeFromSP "<<DumpExpr(exe)<<std::endl;
if(type==DepTree::SAVE) G_tosave[index]->UsedIdents(ids); for(const auto& i:ids)
else if(type==DepTree::PRINT) G_toprint[index]->UsedIdents(ids);
DepTreeVars::const_iterator d;
int ret;
for(auto& i:ids)
{ {
d=vars.find(i); d=dvars.find(i); // Is variable already in tree?
if(d==vars.end()) if(dvars.end()==d) // No
{ {
auto n=*childrens.insert(new DepTree).first; auto n=*childrens.insert(new DepTree).first;
n->parents.insert(this); n->parents.insert(this);
ret=n->CreateNodeFromVar(i,vars,callstack); ret=n->CreateNodeFromVar(i,vars,dvars,callstack);
if(ret!=0) if(ret!=0)
{ {
COUT(ERROR)<<" in "<<((type==DepTree::SAVE)?"save":"print")<<" directive "<<((type==DepTree::SAVE)?G_tosave:G_toprint)[index]->Dump()<<"."<<std::endl; const struct grammatic_location& loc=exe.front().Location();
COUT(ERROR)<<" in "<<((type==DepTree::SAVE)?"save":"print")<<" directive at line "<<loc.first_line<<", position "<<loc.first_column<<std::endl;
for(const auto& inc: loc.incstack) COUT(ERROR)<<" included from "<<inc.filename<<" at line "<<inc.line<<", position "<<inc.column<<std::endl;
return ret; return ret;
} }
} }
else else // Variable already in tree. Just establish links.
{ {
childrens.insert(d->second); childrens.insert(d->second);
d->second->parents.insert(this); d->second->parents.insert(this);
@ -94,8 +105,8 @@ int DepTree::CreateNodeFromSP(DepTree::NodeType list, G_toType::size_type ind, D
return 0; return 0;
} }
// Create dependency tree from expressions to save and print and definitions ov variables
int DepTree::CreateGlobalTree(UsedType& used) int DepTree::CreateTree(ExecType& save, ExecType& print, VarType& vars)
{ {
if(parents.size()!=0) if(parents.size()!=0)
{ {
@ -103,43 +114,42 @@ int DepTree::CreateGlobalTree(UsedType& used)
return 2; return 2;
} }
DepTreeVars vars; DepTreeVars dvars;
type=DepTree::ROOT; type=DepTree::ROOT;
for(G_toType::size_type i=0; i<G_tosave.size(); i++) for(auto& i:save)
{ {
auto n=*childrens.insert(new DepTree).first; auto n=*childrens.insert(new DepTree).first;
n->parents.insert(this); n->parents.insert(this);
auto ret=n->CreateNodeFromSP(DepTree::SAVE,i,vars); auto ret=n->CreateNodeFromSP(DepTree::SAVE,vars,i,dvars);
if(ret!=0) return ret; if(ret!=0) return ret;
} }
for(G_toType::size_type i=0; i<G_toprint.size(); i++) for(auto& i:print)
{ {
auto n=*childrens.insert(new DepTree).first; auto n=*childrens.insert(new DepTree).first;
n->parents.insert(this); n->parents.insert(this);
auto ret=n->CreateNodeFromSP(DepTree::PRINT,i,vars); auto ret=n->CreateNodeFromSP(DepTree::PRINT,vars,i,dvars);
if(ret!=0) return ret; if(ret!=0) return ret;
} }
for(auto& i: vars) used.insert(i.first);
return 0; return 0;
} }
// Get list of nodes without dependencies
DepTree::LeafVector DepTree::FindLeafNodes() const DepTree::LeafVector DepTree::FindLeafNodes() const
{ {
LeafVector leafs; LeafVector leafs;
// Visited nodes
CallStack visited;
std::stack<NodeVector::const_iterator> path,ends; std::stack<NodeVector::const_iterator> path,ends;
NodeVector::const_iterator it,end; NodeVector::const_iterator it,end;
// define isvisited as reversion of last visited
bool isvisited=!visited;
// ascending to root // ascending to root
const DepTree* root=this; const DepTree* root=this;
while(root->parents.size()!=0) root=*(root->parents.begin()); while(root->parents.size()!=0) root=*(root->parents.begin());
const DepTree* curnode=root; const DepTree* curnode=root;
curnode->visited=isvisited; visited.insert(curnode);
it=curnode->childrens.begin(); it=curnode->childrens.begin();
end=curnode->childrens.end(); end=curnode->childrens.end();
@ -147,10 +157,10 @@ DepTree::LeafVector DepTree::FindLeafNodes() const
{ {
if(it!=end) if(it!=end)
{ {
if((*it)->visited==isvisited) {++it; continue;} if(visited.find(*it)!=visited.end()) {++it; continue;}
path.push(it); ends.push(end); path.push(it); ends.push(end);
curnode=(*it); curnode=(*it);
curnode->visited=isvisited; visited.insert(curnode);
it=curnode->childrens.begin(); it=curnode->childrens.begin();
end=curnode->childrens.end(); end=curnode->childrens.end();
if(it==end) leafs.push_back(const_cast<DepTree*>(curnode)); if(it==end) leafs.push_back(const_cast<DepTree*>(curnode));
@ -168,14 +178,102 @@ DepTree::LeafVector DepTree::FindLeafNodes() const
} }
// Dump tree contents
void DepTree::DumpTree() const
{
// Visited nodes
CallStack visited;
std::stack<NodeVector::const_iterator> path,ends;
NodeVector::const_iterator it,end;
// ascending to root
const DepTree* root=this;
while(root->parents.size()!=0) root=*(root->parents.begin());
const DepTree* curnode=root;
visited.insert(curnode);
it=curnode->childrens.begin();
end=curnode->childrens.end();
while(true)
{
if(it!=end)
{
if(visited.find(*it)!=visited.end()) {++it; continue;}
path.push(it); ends.push(end);
curnode=(*it);
visited.insert(curnode);
if(SAVE==curnode->type) COUT(NORMAL)<<"save" <<DumpExpr(curnode->exe)<<";"<<std::endl;
if(PRINT==curnode->type) COUT(NORMAL)<<"print"<<DumpExpr(curnode->exe)<<";"<<std::endl;
if(VAR==curnode->type) COUT(NORMAL)<<curnode->name<<"="<<DumpExpr(curnode->exe)<<";"<<std::endl;
it=curnode->childrens.begin();
end=curnode->childrens.end();
}
else
{
if(path.size()==0) break;
it=path.top(); path.pop();
end=ends.top(); ends.pop();
++it;
}
}
}
// Check if all functions are defined
int DepTree::CheckFunctions() const
{
// Visited nodes
CallStack visited;
std::stack<NodeVector::const_iterator> path,ends;
NodeVector::const_iterator it,end;
// ascending to root
const DepTree* root=this;
while(root->parents.size()!=0) root=*(root->parents.begin());
const DepTree* curnode=root;
visited.insert(curnode);
it=curnode->childrens.begin();
end=curnode->childrens.end();
while(true)
{
if(it!=end)
{
if(visited.find(*it)!=visited.end()) {++it; continue;}
path.push(it); ends.push(end);
curnode=(*it);
visited.insert(curnode);
for(const auto& se: curnode->exe) if(StackElem::TYPE_FUNCTION==se.T() && G_funcs.find(se.Name())==G_funcs.end())
{
const struct grammatic_location& loc=se.Location();
COUT(ERROR)<<"Unknown function "<<se.Name()<<" at line "<<loc.first_line<<", position "<<loc.first_column<<std::endl;
for(const auto& inc: loc.incstack) COUT(ERROR)<<" included from "<<inc.filename<<" at line "<<inc.line<<", position "<<inc.column<<std::endl;
return 1;
}
it=curnode->childrens.begin();
end=curnode->childrens.end();
}
else
{
if(path.size()==0) break;
it=path.top(); path.pop();
end=ends.top(); ends.pop();
++it;
}
}
return 0;
}
// Multi-threaded version // Multi-threaded version
void* TreeEvaluateM(void* arg) void* TreeEvaluateM(void* arg)
{ {
struct timespec skip={0,10000000}; struct timespec skip={0,10000000};
DepTree::thread_params* p=reinterpret_cast<DepTree::thread_params*>(arg); DepTree::thread_params* p=reinterpret_cast<DepTree::thread_params*>(arg);
DepTree* leaf; DepTree* leaf;
ObjectBase *ob,*eob;
ObjectList* ol;
bool err; bool err;
while(true) while(true)
@ -183,14 +281,14 @@ void* TreeEvaluateM(void* arg)
// Begin critical section (access to root) // Begin critical section (access to root)
pthread_mutex_lock(&p->root_mtx); pthread_mutex_lock(&p->root_mtx);
// Check, if all done, or error happens? // Check, if all done, or error happens?
if(0==p->root->childrens.size() || 0!=p->exitcode) {pthread_mutex_unlock(&p->root_mtx); return 0;} if(0==p->root->childrens.size() || 0!=p->exitcode) {pthread_mutex_unlock(&p->root_mtx); return nullptr;}
// End critical section // End critical section
pthread_mutex_unlock(&p->root_mtx); pthread_mutex_unlock(&p->root_mtx);
// Begin critical section (access to leafs) // Begin critical section (access to leafs)
pthread_mutex_lock(&p->leaf_mtx); pthread_mutex_lock(&p->leaf_mtx);
// Check, if some work available? // Check, if some work available?
if(0==p->leafs.size()) {pthread_mutex_unlock(&p->leaf_mtx); nanosleep(&skip,0); continue;} if(0==p->leafs.size()) {pthread_mutex_unlock(&p->leaf_mtx); nanosleep(&skip,nullptr); continue;}
// Select working node // Select working node
leaf=*(p->leafs.begin()); leaf=*(p->leafs.begin());
// and remove its from list // and remove its from list
@ -202,23 +300,19 @@ void* TreeEvaluateM(void* arg)
{ {
err=false; err=false;
// Begin critical section (access to G_toprint or G_tosave) std::unique_ptr<const ObjectList> ol(dynamic_cast<const ObjectList*>(Evaluate(leaf->exe,&err))); // No check for return value, trust in grammatical parser
pthread_mutex_lock(&p->prsv_mtx);
ol=G_tosave[leaf->index];
// End critical section
pthread_mutex_unlock(&p->prsv_mtx);
ol->Evaluate(&err); // For list Evaluate always return 0;
if(err) if(err)
{ {
COUT(ERROR)<<" in instruction save"<<ol->Dump()<<")"<<std::endl; const struct grammatic_location& loc=leaf->exe.front().Location();
COUT(ERROR)<<" in instruction save at line "<<loc.first_line<<", position "<<loc.first_column<<std::endl;
for(const auto& inc: loc.incstack) COUT(ERROR)<<" included from "<<inc.filename<<" at line "<<inc.line<<", position "<<inc.column<<std::endl;
p->exitcode=1; p->exitcode=1;
return 0; return nullptr;
} }
if(!Save(ol)) if(!Save(ol.get()))
{ {
p->exitcode=1; p->exitcode=1;
return 0; return nullptr;
} }
// This leaf can have only one parent - root // This leaf can have only one parent - root
// Begin critical section (access to root) // Begin critical section (access to root)
@ -232,23 +326,19 @@ void* TreeEvaluateM(void* arg)
{ {
err=false; err=false;
// Begin critical section (access to G_toprint or G_tosave) std::unique_ptr<const ObjectList> ol(dynamic_cast<const ObjectList*>(Evaluate(leaf->exe,&err))); // No check for return value, trust in grammatical parser
pthread_mutex_lock(&p->prsv_mtx);
ol=G_toprint[leaf->index];
// End critical section
pthread_mutex_unlock(&p->prsv_mtx);
ol->Evaluate(&err); // For list Evaluate always return 0;
if(err) if(err)
{ {
COUT(ERROR)<<" in instruction print"<<ol->Dump()<<")"<<std::endl; const struct grammatic_location& loc=leaf->exe.front().Location();
COUT(ERROR)<<" in instruction print at line "<<loc.first_line<<", position "<<loc.first_column<<std::endl;
for(const auto& inc: loc.incstack) COUT(ERROR)<<" included from "<<inc.filename<<" at line "<<inc.line<<", position "<<inc.column<<std::endl;
p->exitcode=1; p->exitcode=1;
return 0; return nullptr;
} }
if(!Print(ol)) if(!Print(ol.get()))
{ {
p->exitcode=1; p->exitcode=1;
return 0; return nullptr;
} }
// Begin critical section (access to root) // Begin critical section (access to root)
@ -257,32 +347,24 @@ void* TreeEvaluateM(void* arg)
(*leaf->parents.begin())->childrens.erase(leaf); (*leaf->parents.begin())->childrens.erase(leaf);
// End critical section // End critical section
pthread_mutex_unlock(&p->root_mtx); pthread_mutex_unlock(&p->root_mtx);
} }
if(DepTree::VAR==leaf->type) if(DepTree::VAR==leaf->type)
{ {
// Begin critical section (access to G_vars) const ObjectBase *eob;
pthread_mutex_lock(&p->vars_mtx);
ob=G_vars.at(leaf->name);
G_vars.erase(leaf->name);
// End critical section
pthread_mutex_unlock(&p->vars_mtx);
// Main working call // Main working call
err=false; err=false;
eob=ob->Evaluate(&err); eob=Evaluate(leaf->exe,&err);
if(err) if(err)
{ {
COUT(ERROR)<<" in definition of variable "<<leaf->name<<std::endl; const struct grammatic_location& loc=leaf->exe.front().Location();
COUT(ERROR)<<" in definition of variable "<<leaf->name<<" at line "<<loc.first_line<<", position "<<loc.first_column<<std::endl;
for(const auto& inc: loc.incstack) COUT(ERROR)<<" included from "<<inc.filename<<" at line "<<inc.line<<", position "<<inc.column<<std::endl;
p->exitcode=1; p->exitcode=1;
return 0; return nullptr;
} }
// eob is evaluated object
if(0!=eob) delete ob;
else eob=ob;
// Begin critical section (access to tree structure) // Begin critical section (access to tree structure)
pthread_mutex_lock(&p->tree_mtx); pthread_mutex_lock(&p->tree_mtx);
for(auto& i:leaf->parents) for(auto& i:leaf->parents)
@ -290,35 +372,7 @@ void* TreeEvaluateM(void* arg)
// leaf not children of anyone // leaf not children of anyone
i->childrens.erase(leaf); i->childrens.erase(leaf);
// Replace variable on eob // Replace variable on eob
if(DepTree::SAVE==i->type) ReplaceVar(i->exe,leaf->name,eob);
{
// Begin critical section (access to G_toprint or G_tosave)
pthread_mutex_lock(&p->prsv_mtx);
G_tosave[i->index]->ReplaceVar(leaf->name,eob); // ReplaceVar always return 0 for ObjectList
// End critical section
pthread_mutex_unlock(&p->prsv_mtx);
}
if(DepTree::PRINT==i->type)
{
// Begin critical section (access to G_toprint or G_tosave)
pthread_mutex_lock(&p->prsv_mtx);
G_toprint[i->index]->ReplaceVar(leaf->name,eob); // ReplaceVar always return 0 for ObjectList
// End critical section
pthread_mutex_unlock(&p->prsv_mtx);
}
if(DepTree::VAR==i->type)
{
// Begin critical section (access to G_vars)
pthread_mutex_lock(&p->vars_mtx);
ob=G_vars[i->name]->ReplaceVar(leaf->name,eob);
if(0!=ob)
{
delete G_vars[i->name];
G_vars[i->name]=ob;
}
// End critical section
pthread_mutex_unlock(&p->vars_mtx);
}
// Begin critical section (access to leafs) // Begin critical section (access to leafs)
pthread_mutex_lock(&p->leaf_mtx); pthread_mutex_lock(&p->leaf_mtx);
@ -335,7 +389,7 @@ void* TreeEvaluateM(void* arg)
leaf->parents.clear(); leaf->parents.clear();
delete leaf; delete leaf;
} }
return 0; return nullptr;
} }
@ -344,9 +398,8 @@ void* TreeEvaluate(void* arg)
{ {
DepTree::thread_params* p=reinterpret_cast<DepTree::thread_params*>(arg); DepTree::thread_params* p=reinterpret_cast<DepTree::thread_params*>(arg);
DepTree* leaf; DepTree* leaf;
ObjectBase *ob,*eob;
ObjectList* ol;
bool err; bool err;
while(0!=p->leafs.size()) while(0!=p->leafs.size())
{ {
// Select working node // Select working node
@ -357,19 +410,21 @@ void* TreeEvaluate(void* arg)
if(DepTree::SAVE==leaf->type) if(DepTree::SAVE==leaf->type)
{ {
err=false; err=false;
ol=G_tosave[leaf->index];
ol->Evaluate(&err); // For list Evaluate always return 0; std::unique_ptr<const ObjectList> ol(dynamic_cast<const ObjectList*>(Evaluate(leaf->exe,&err))); // No check for return value, trust in grammatical parser
if(err) if(err)
{ {
COUT(ERROR)<<" in instruction save"<<ol->Dump()<<")"<<std::endl; const struct grammatic_location& loc=leaf->exe.front().Location();
COUT(ERROR)<<" in instruction save at line "<<loc.first_line<<", position "<<loc.first_column<<std::endl;
for(const auto& inc: loc.incstack) COUT(ERROR)<<" included from "<<inc.filename<<" at line "<<inc.line<<", position "<<inc.column<<std::endl;
p->exitcode=1; p->exitcode=1;
return 0; return nullptr;
} }
// eob is evaluated object // eob is evaluated object
if(!Save(ol)) if(!Save(ol.get()))
{ {
p->exitcode=1; p->exitcode=1;
return 0; return nullptr;
} }
// This leaf can have only one parent - root // This leaf can have only one parent - root
(*leaf->parents.begin())->childrens.erase(leaf); (*leaf->parents.begin())->childrens.erase(leaf);
@ -378,18 +433,20 @@ void* TreeEvaluate(void* arg)
if(DepTree::PRINT==leaf->type) if(DepTree::PRINT==leaf->type)
{ {
err=false; err=false;
ol=G_toprint[leaf->index];
ol->Evaluate(&err); // For list Evaluate always return 0; std::unique_ptr<const ObjectList> ol(dynamic_cast<const ObjectList*>(Evaluate(leaf->exe,&err))); // No check for return value, trust in grammatical parser
if(err) if(err)
{ {
COUT(ERROR)<<" in instruction print"<<ol->Dump()<<")"<<std::endl; const struct grammatic_location& loc=leaf->exe.front().Location();
COUT(ERROR)<<" in instruction print at line "<<loc.first_line<<", position "<<loc.first_column<<std::endl;
for(const auto& inc: loc.incstack) COUT(ERROR)<<" included from "<<inc.filename<<" at line "<<inc.line<<", position "<<inc.column<<std::endl;
p->exitcode=1; p->exitcode=1;
return 0; return nullptr;
} }
if(!Print(ol)) if(!Print(ol.get()))
{ {
p->exitcode=1; p->exitcode=1;
return 0; return nullptr;
} }
// This leaf can have only one parent - root // This leaf can have only one parent - root
(*leaf->parents.begin())->childrens.erase(leaf); (*leaf->parents.begin())->childrens.erase(leaf);
@ -397,34 +454,26 @@ void* TreeEvaluate(void* arg)
if(DepTree::VAR==leaf->type) if(DepTree::VAR==leaf->type)
{ {
err=false; const ObjectBase *eob;
ob=G_vars.at(leaf->name);
G_vars.erase(leaf->name);
// Main working call // Main working call
eob=ob->Evaluate(&err); err=false;
eob=Evaluate(leaf->exe,&err);
if(err) if(err)
{ {
COUT(ERROR)<<" in definition of variable "<<leaf->name<<std::endl; const struct grammatic_location& loc=leaf->exe.front().Location();
COUT(ERROR)<<" in definition of variable "<<leaf->name<<" at line "<<loc.first_line<<", position "<<loc.first_column<<std::endl;
for(const auto& inc: loc.incstack) COUT(ERROR)<<" included from "<<inc.filename<<" at line "<<inc.line<<", position "<<inc.column<<std::endl;
p->exitcode=1; p->exitcode=1;
return 0; return nullptr;
} }
// eob is evaluated object
if(0!=eob) delete ob;
else eob=ob;
//G_vars.erase(leaf->name);
for(auto& i:leaf->parents) for(auto& i:leaf->parents)
{ {
// leaf not children of anyone // leaf not children of anyone
i->childrens.erase(leaf); i->childrens.erase(leaf);
// Replace variable on eob // Replace variable on eob
if(DepTree::SAVE==i->type) G_tosave[i->index]->ReplaceVar(leaf->name,eob); // ReplaceVar always return 0 for ObjectList ReplaceVar(i->exe,leaf->name,eob);
if(DepTree::PRINT==i->type) G_toprint[i->index]->ReplaceVar(leaf->name,eob);
if(DepTree::VAR==i->type)
{
ob=G_vars[i->name]->ReplaceVar(leaf->name,eob);
if(0!=ob) {delete G_vars[i->name]; G_vars[i->name]=ob;}
}
// If node have no children, it's a new leaf node // If node have no children, it's a new leaf node
if(0==i->childrens.size() && DepTree::ROOT!=i->type) p->leafs.push_back(i); if(0==i->childrens.size() && DepTree::ROOT!=i->type) p->leafs.push_back(i);
} }
@ -433,7 +482,7 @@ void* TreeEvaluate(void* arg)
leaf->parents.clear(); leaf->parents.clear();
delete leaf; delete leaf;
} }
return 0; return nullptr;
} }
@ -449,21 +498,17 @@ int DepTree::EvaluateTree(unsigned int nthreads)
{ {
pthread_t* threads=new pthread_t[nthreads-1]; pthread_t* threads=new pthread_t[nthreads-1];
p.root=this; p.root=this;
pthread_mutex_init(&p.leaf_mtx,0); pthread_mutex_init(&p.leaf_mtx,nullptr);
pthread_mutex_init(&p.root_mtx,0); pthread_mutex_init(&p.root_mtx,nullptr);
pthread_mutex_init(&p.vars_mtx,0); pthread_mutex_init(&p.tree_mtx,nullptr);
pthread_mutex_init(&p.prsv_mtx,0);
pthread_mutex_init(&p.tree_mtx,0);
while(0!=p.root->parents.size()) p.root=*(p.root->parents.begin()); while(0!=p.root->parents.size()) p.root=*(p.root->parents.begin());
for(unsigned int i=0;i<nthreads-1;++i) pthread_create(threads+i,0,&TreeEvaluateM,&p); for(unsigned int i=0;i<nthreads-1;++i) pthread_create(threads+i,nullptr,&TreeEvaluateM,&p);
TreeEvaluateM(&p); TreeEvaluateM(&p);
for(unsigned int i=0;i<nthreads-1;++i) pthread_join(threads[i],0); for(unsigned int i=0;i<nthreads-1;++i) pthread_join(threads[i],nullptr);
delete[] threads; delete[] threads;
pthread_mutex_destroy(&p.leaf_mtx); pthread_mutex_destroy(&p.leaf_mtx);
pthread_mutex_destroy(&p.root_mtx); pthread_mutex_destroy(&p.root_mtx);
pthread_mutex_destroy(&p.vars_mtx);
pthread_mutex_destroy(&p.prsv_mtx);
pthread_mutex_destroy(&p.tree_mtx); pthread_mutex_destroy(&p.tree_mtx);
} }
return p.exitcode; return p.exitcode;

86
src/globals.cpp

@ -1,30 +1,14 @@
#include "globals.h" #include "globals.h"
// Variables definitions
G_varsType G_vars;
// Functions addresses // Functions addresses
G_funcsType G_funcs; G_funcsType G_funcs;
// List of objects to save
G_toType G_tosave;
// List of objects to print
G_toType G_toprint;
// Loaded modules // Loaded modules
G_libsType G_libs; G_libsType G_libs;
void ClearGlobals() void ClearGlobals()
{ {
for(auto& it:G_vars) delete it.second;
for(auto& it:G_tosave) delete it;
for(auto& it:G_toprint) delete it;
for(auto& it:G_libs) dlclose(it); for(auto& it:G_libs) dlclose(it);
G_vars.clear();
G_tosave.clear();
G_toprint.clear();
} }
@ -48,20 +32,20 @@ int LoadModule(const std::string& name, const void* p, const std::string& modnam
initname+="_module_init"; initname+="_module_init";
// Check if module is statically linked or already loaded: dlopen'ed the main program // Check if module is statically linked or already loaded: dlopen'ed the main program
handle=dlopen(0,RTLD_LAZY|RTLD_GLOBAL); handle=dlopen(nullptr,RTLD_LAZY|RTLD_GLOBAL);
if(0==handle) if(nullptr==handle)
{ {
COUT(ERROR)<<std::endl<<"Something wrong: can't dlopen the main program"<<std::endl; COUT(ERROR)<<std::endl<<"Something wrong: can't dlopen the main program"<<std::endl;
return 1; return 1;
} }
initfunc=dlsym(handle,initname.c_str()); initfunc=dlsym(handle,initname.c_str());
if(0==initfunc) // We not find it if(nullptr==initfunc) // We not find it
{ {
if('/'==name[0]) // Absolute path if('/'==name[0]) // Absolute path
{ {
handle=dlopen(name.c_str(),RTLD_LAZY|RTLD_GLOBAL); handle=dlopen(name.c_str(),RTLD_LAZY|RTLD_GLOBAL);
if(0==handle) handle=dlopen((name+".so").c_str(),RTLD_LAZY|RTLD_GLOBAL); if(nullptr==handle) handle=dlopen((name+".so").c_str(),RTLD_LAZY|RTLD_GLOBAL);
} }
else // Relative path else // Relative path
{ {
@ -73,13 +57,13 @@ int LoadModule(const std::string& name, const void* p, const std::string& modnam
epos=spath.find(':',bpos); epos=spath.find(':',bpos);
curpath=spath.substr(bpos,(std::string::npos==epos)?epos:(epos-bpos)); curpath=spath.substr(bpos,(std::string::npos==epos)?epos:(epos-bpos));
handle=dlopen((curpath+name).c_str(),RTLD_LAZY|RTLD_GLOBAL); handle=dlopen((curpath+name).c_str(),RTLD_LAZY|RTLD_GLOBAL);
if(0!=handle) break; if(nullptr!=handle) break;
handle=dlopen((curpath+name+".so").c_str(),RTLD_LAZY|RTLD_GLOBAL); handle=dlopen((curpath+name+".so").c_str(),RTLD_LAZY|RTLD_GLOBAL);
if(0!=handle) break; if(nullptr!=handle) break;
bpos=epos+1; bpos=epos+1;
} while(std::string::npos!=epos); } while(std::string::npos!=epos);
} }
if(0==handle) if(nullptr==handle)
{ {
COUT(ERROR)<<std::endl<<"Can't dlopen module "<<name<<std::endl; COUT(ERROR)<<std::endl<<"Can't dlopen module "<<name<<std::endl;
return 1; return 1;
@ -88,7 +72,7 @@ int LoadModule(const std::string& name, const void* p, const std::string& modnam
initfunc=dlsym(handle,initname.c_str()); initfunc=dlsym(handle,initname.c_str());
} }
if(0==initfunc) if(nullptr==initfunc)
{ {
COUT(ERROR)<<std::endl<<"Can't find initialising function "<<initname<<" in module "<<name<<std::endl; COUT(ERROR)<<std::endl<<"Can't find initialising function "<<initname<<" in module "<<name<<std::endl;
return 1; return 1;
@ -108,7 +92,7 @@ void RegisterFunction(const std::string& name, Func func)
bool Save(const ObjectList* input) bool Save(const ObjectList* input)
{ {
ObjectList::ListValues::size_type sz=input->Size(), i; ObjectList::IndexType sz=input->Size(), i;
if(sz<2 || sz%2==1 ) if(sz<2 || sz%2==1 )
{ {
COUT(ERROR)<<"Number of save arguments must not be "<<sz<<std::endl; COUT(ERROR)<<"Number of save arguments must not be "<<sz<<std::endl;
@ -116,12 +100,9 @@ bool Save(const ObjectList* input)
} }
// Check arguments types // Check arguments types
const ObjectBase* arg1;
const ObjectString* arg2;
for(i=0;i<sz/2;i++) for(i=0;i<sz/2;i++)
{ {
arg1=input->At(i*2+1); if(!OBType<ObjectString>(input->At(i*2+1)))
if(!IS_OTYPE(arg1,ObjectString))
{ {
COUT(ERROR)<<"Save format is save(object_1,file_1,...,object_n,file_n) where file_i is string"<<std::endl; COUT(ERROR)<<"Save format is save(object_1,file_1,...,object_n,file_n) where file_i is string"<<std::endl;
return false; return false;
@ -129,10 +110,11 @@ bool Save(const ObjectList* input)
} }
// Save // Save
const ObjectBase* arg1;
for(i=0;i<sz/2;i++) for(i=0;i<sz/2;i++)
{ {
arg1=input->At(i*2); arg1=input->At(i*2);
arg2=dynamic_cast<const ObjectString*>(input->At(i*2+1)); OBType<ObjectString> arg2(input->At(i*2+1));
if(!arg1->Save(arg2->Value().c_str())) if(!arg1->Save(arg2->Value().c_str()))
{ {
COUT(ERROR)<<"Can't save object "<<arg1->Dump()<<" to file "<<arg2->Value()<<std::endl; COUT(ERROR)<<"Can't save object "<<arg1->Dump()<<" to file "<<arg2->Value()<<std::endl;
@ -146,7 +128,7 @@ bool Save(const ObjectList* input)
bool Print(const ObjectList* input) bool Print(const ObjectList* input)
{ {
ObjectList::ListValues::size_type sz=input->Size(), i; ObjectList::IndexType sz=input->Size(), i;
if(sz==0) return true; if(sz==0) return true;
// Print // Print
@ -161,3 +143,45 @@ bool Print(const ObjectList* input)
return true; return true;
} }
WordList Split(const std::string& str, const std::string& delims, bool allowempty)
{
WordList wl;
size_t pos=0,bpos;
if(0==str.size()) return wl;
if(0==delims.size())
{
wl.push_back(str);
return wl;
}
if(allowempty)
{
// Find first delimiter symbol
pos=str.find_first_of(delims);
wl.push_back(str.substr(0,pos)); // pos can be npos
while(std::string::npos!=pos)
{
bpos=pos+1;
pos=str.find_first_of(delims,bpos); // bpos can be greater then length
wl.push_back(str.substr(bpos,(std::string::npos==pos)?pos:(pos-bpos)));
}
}
else
{
while(true)
{
// Find first nondelimiter symbol
bpos=str.find_first_not_of(delims,pos);
if(std::string::npos==bpos) break; // no nondelimiters
// Find first delimiter symbol
pos=str.find_first_of(delims,bpos);
wl.push_back(str.substr(bpos,(std::string::npos==pos)?pos:(pos-bpos)));
if(std::string::npos!=pos) pos++;
else break;
}
}
return wl;
}

73
src/init.cpp

@ -17,59 +17,7 @@ typedef void* yyscan_t;
#undef YYSTYPE #undef YYSTYPE
#undef YYLTYPE #undef YYLTYPE
int BuildDepTree(DepTree* deptree, UsedType& used) {return deptree->CreateGlobalTree(used);} int ParseConfigFile(const char* config, ExecType& tosave, ExecType& toprint, VarType& vars)
int CheckFunctions()
{
UsedType funcs;
for(auto& i:G_tosave)
{
i->UsedFuncs(funcs);
for(auto& f:funcs) if(G_funcs.find(f)==G_funcs.end())
{
COUT(ERROR)<<"Unknown function "<<f<<" in directive save"<<i->Dump()<<std::endl;
return 1;
}
funcs.clear();
}
for(auto& i:G_toprint)
{
i->UsedFuncs(funcs);
for(auto& f:funcs) if(G_funcs.find(f)==G_funcs.end())
{
COUT(ERROR)<<"Unknown function "<<f<<" in directive print"<<i->Dump()<<std::endl;
return 1;
}
funcs.clear();
}
for(auto& i:G_vars)
{
i.second->UsedFuncs(funcs);
for(auto& f:funcs) if(G_funcs.find(f)==G_funcs.end())
{
COUT(ERROR)<<"Unknown function "<<f<<" in definition of variable "<<i.first<<std::endl;
return 1;
}
funcs.clear();
}
return 0;
}
void DumpConfig()
{
for(auto& i: G_vars) COUT(NORMAL)<<i.first<<"="+i.second->Dump()<<";"<<std::endl;
for(auto& i: G_tosave) COUT(NORMAL)<<"save"<<i->Dump()<<";"<<std::endl;
for(auto& i: G_toprint) COUT(NORMAL)<<"print"<<i->Dump()<<";"<<std::endl;
}
int ParseConfigFile(const char* config)
{ {
yyscan_t scanner; yyscan_t scanner;
struct lexical_extra extra; struct lexical_extra extra;
@ -77,7 +25,7 @@ int ParseConfigFile(const char* config)
int ret; int ret;
conffd=fopen(config,"r"); conffd=fopen(config,"r");
if(conffd==0) if(nullptr==conffd)
{ {
COUT(ERROR)<<"Can't open file "<<config<<std::endl; COUT(ERROR)<<"Can't open file "<<config<<std::endl;
return 1; return 1;
@ -91,6 +39,9 @@ int ParseConfigFile(const char* config)
char* cwd=get_current_dir_name(); char* cwd=get_current_dir_name();
extra.state.curdir=cwd; extra.state.curdir=cwd;
free(cwd); free(cwd);
extra.toprint=&toprint;
extra.tosave=&tosave;
extra.vars=&vars;
extra.ParsePath(config); extra.ParsePath(config);
// Search paths begin from working directory // Search paths begin from working directory
extra.includedirs=extra.moduledirs="./"; extra.includedirs=extra.moduledirs="./";
@ -134,13 +85,13 @@ int ParseOptions(int argc, char** argv, struct program_options& options)
int RegisterBuiltinFunctions() int RegisterBuiltinFunctions()
{ {
RegisterFunction("ADD",Arifm_Add); RegisterFunction("ADD",Arifm2<OpAdd>);
RegisterFunction("SUB",Arifm_Sub); RegisterFunction("SUB",Arifm2<OpSub>);
RegisterFunction("MUL",Arifm_Mul); RegisterFunction("MUL",Arifm2<OpMul>);
RegisterFunction("DIV",Arifm_Div); RegisterFunction("DIV",Arifm2<OpDiv>);
RegisterFunction("POW",Arifm_Pow); RegisterFunction("POW",Arifm2<OpPow,false>);
RegisterFunction("POS",Arifm_Pos); RegisterFunction("NEG",Arifm1<OpNeg>);
RegisterFunction("NEG",Arifm_Neg); RegisterFunction("POS",Arifm1<OpPos>);
RegisterFunction("GET",Get<ObjectList>); RegisterFunction("GET",Get<ObjectList>);
RegisterFunction("GET",Get<ObjectPair>); RegisterFunction("GET",Get<ObjectPair>);

42
src/main.cpp

@ -14,63 +14,59 @@ static void usage(const char* prg)
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
int ret; int ret=0;
DepTree* DPTree; DepTree* DPTree=nullptr;
struct program_options options; struct program_options options;
// Set default options // Set default options
options.threads=1; options.threads=1;
options.help=options.dump=false; options.help=options.dump=false;
options.dl=NORMAL; options.dl=NORMAL;
options.config=0; options.config=nullptr;
// Parse options // Parse options
ret=ParseOptions(argc,argv,options); ret=ParseOptions(argc,argv,options);
if(ret!=0 || options.config==0) {usage(argv[0]); return 1;} if(ret!=0 || nullptr==options.config) {usage(argv[0]); return 1;}
if(options.help) {usage(argv[0]); return 0;} if(options.help) {usage(argv[0]); return 0;}
SetDebugLevel(options.dl); SetDebugLevel(options.dl);
RegisterBuiltinFunctions();
{
VarType vars;
ExecType ToPrint, ToSave;
COUT(INFO)<<"Parse config file "<<options.config<<" "; COUT(INFO)<<"Parse config file "<<options.config<<" ";
ret=ParseConfigFile(options.config); if(ret!=0) { ClearGlobals(); return 1;} ret=ParseConfigFile(options.config,ToSave,ToPrint,vars); if(ret!=0) { ClearGlobals(); return 1;}
COUT(INFO)<<"Ok"<<std::endl; COUT(INFO)<<"Ok"<<std::endl;
if(G_tosave.size()==0 && G_toprint.size()==0) if(ToSave.size()==0 && ToPrint.size()==0)
{ {
COUT(WARNING)<<"No actions needed, exiting"<<std::endl; COUT(WARNING)<<"No actions needed, exiting"<<std::endl;
ClearGlobals(); ClearGlobals();
return 0; return 0;
} }
{
UsedType used, all;
for(auto& i:G_vars) all.insert(i.first);
unsigned int tot=G_vars.size();
DPTree=new DepTree; DPTree=new DepTree;
COUT(INFO)<<"Building dependency tree "; COUT(INFO)<<"Building dependency tree ";
ret=BuildDepTree(DPTree,used); if(ret!=0) { ClearGlobals(); delete DPTree; return 1;} ret=DPTree->CreateTree(ToSave,ToPrint,vars);
if(ret!=0) goto end;
COUT(INFO)<<"Ok"<<std::endl; COUT(INFO)<<"Ok"<<std::endl;
COUT(INFO)<<"Remove unneeded definitions: ";
for(auto& i:all) if(used.count(i)==0) { delete G_vars[i]; G_vars.erase(i);};
COUT(INFO)<<(tot-G_vars.size())<<" removed, "<<G_vars.size()<<" remains."<<std::endl;
} }
RegisterBuiltinFunctions();
COUT(INFO)<<"Checking functions "; COUT(INFO)<<"Checking functions ";
ret=CheckFunctions(); if(ret!=0) { ClearGlobals(); delete DPTree; return 1;} ret=DPTree->CheckFunctions(); if(ret!=0) goto end;
COUT(INFO)<<"Ok"<<std::endl; COUT(INFO)<<"Ok"<<std::endl;
if(options.dump) DumpConfig(); if(options.dump) DPTree->DumpTree();
else else
{ {
COUT(INFO)<<"Evaluate tree "; COUT(INFO)<<"Evaluate tree ";
ret=DPTree->EvaluateTree(options.threads); if(ret!=0) { ClearGlobals(); delete DPTree; return 1;} ret=DPTree->EvaluateTree(options.threads); if(ret!=0) goto end;
COUT(INFO)<<"Ok"<<std::endl; COUT(INFO)<<"Ok"<<std::endl;
} }
end:
ClearGlobals(); ClearGlobals();
delete DPTree; if(nullptr!=DPTree) delete DPTree;
return 0; return (0==ret)?0:1;
} }

230
src/object.cpp

@ -6,6 +6,204 @@ template<> EXPORT std::string ObjectSimple<int64_t>::type="integer";
template<> EXPORT std::string ObjectSimple<double>::type="real"; template<> EXPORT std::string ObjectSimple<double>::type="real";
template<> EXPORT std::string ObjectSimple<std::string>::type="string"; template<> EXPORT std::string ObjectSimple<std::string>::type="string";
std::string DumpExprE(const ExecExpr& exp)
{
std::string ret;
for(const auto& i: exp)
{
ret+="Element: ";
switch(i.T())
{
case(StackElem::TYPE_BEGINLIST): {ret+="("; break;}
case(StackElem::TYPE_ENDLIST): {ret+=")"; break;}
case(StackElem::TYPE_MKPAIR): {ret+="MKPAIR: "+i.Name(); break;}
case(StackElem::TYPE_OBJECT): {ret+="OBJECT: "+i.Object()->Type()+" "+i.Object()->Dump(); break;}
case(StackElem::TYPE_VARIABLE): {ret+="VARIABLE: "+i.Name(); break;}
case(StackElem::TYPE_FUNCTION): {ret+="FUNCTION: "+i.Name(); break;}
default: ret+=" %ERROR% ";
}
ret+="\n";
ret+="Location: "+i.Location().filename+" "+ToString(i.Location().first_line)+" "+ToString(i.Location().first_column)+" "+ToString(i.Location().incstack.size())+"\n\n";
}
return ret;
}
std::string DumpExpr(const ExecExpr& exp)
{
std::list<std::string> stack;
std::string ret;
for(const auto& i: exp)
{
switch(i.T())
{
case(StackElem::TYPE_BEGINLIST): {stack.push_back("("); break;}
case(StackElem::TYPE_ENDLIST): {stack.push_back(")"); break;}
case(StackElem::TYPE_MKPAIR):
{
if(")"!=stack.back()) {auto s=stack.crbegin(); s++; stack.insert(s.base(),i.Name()+"=");}
else
{
ret.clear();
uint n=0;
for(auto s=stack.crbegin(); s!=stack.crend();++s)
{
if(ret[0]!=')' && *s!="(" && *s!=")") ret=(*s)+","+ret;
else ret=(*s)+ret;
if(")"==*s) n++;
if("("==*s)
{
n--;
if(0==n)
{
stack.erase((++s).base(),stack.end());
stack.push_back(i.Name()+"="+ret);
break;
}
}
}
}
break;
}
case(StackElem::TYPE_OBJECT): {stack.push_back(i.Object()->Dump()); break;}
case(StackElem::TYPE_VARIABLE): {stack.push_back(i.Name()); break;}
case(StackElem::TYPE_FUNCTION):
{
ret.clear();
uint n=0;
for(auto s=stack.crbegin(); s!=stack.crend();++s)
{
if(ret[0]!=')' && *s!="(" && *s!=")") ret=(*s)+","+ret;
else ret=(*s)+ret;
if(")"==*s) n++;
if("("==*s)
{
n--;
if(0==n)
{
stack.erase((++s).base(),stack.end());
stack.push_back(i.Name()+ret);
break;
}
}
}
break; // Never reach this line
}
default: stack.push_back(" %ERROR% ");
}
}
ret.clear();
for(const auto& s: stack)
{
if('('==ret.back() || '='==ret.back() || ')'==s.front() || ret.empty()) ret+=s;
else ret+=","+s;
}
return ret;
}
UsedType UsedVars(const ExecExpr& exp)
{
UsedType ret;
for(const auto& i: exp) if(StackElem::TYPE_VARIABLE==i.T()) ret.insert(i.Name());
return ret;
}
UsedType UsedFuncs(const ExecExpr& exp)
{
UsedType ret;
for(const auto& i: exp) if(StackElem::TYPE_FUNCTION==i.T()) ret.insert(i.Name());
return ret;
}
const ObjectBase* Evaluate(ExecExpr& exp, bool* err)
{
ExecExpr::iterator cse=exp.begin();
while(cse!=exp.end())
{
switch(cse->T())
{
case(StackElem::TYPE_ENDLIST): // Making list
{
ObjectList* ol=new ObjectList;
ExecExpr::iterator bl,el;
bl=el=cse++;
bl--;
// Reverse iteration to fill list. We know that BEGINLIST exists before ENDLIST, because grammatical parser already check it.
while(StackElem::TYPE_BEGINLIST!=bl->T()) {ol->PushFront(bl->PickObject()); bl--;}
// Remove all elements from BEGINLIST to ENDLIST, but not ENDLIST itself
exp.erase(bl,el);
// Replace ENDLIST by ObjectList
el->ReplaceByObject(ol);
// Go to next element
continue;
}
case(StackElem::TYPE_MKPAIR):
{
// Previous element must be OBJECT. We don't check this, we trust in grammatical parser.
ExecExpr::iterator pr=cse;
pr--;
cse->ReplaceByObject(new ObjectPair(cse->Name(),pr->PickObject()));
// Remove previous element
exp.erase(pr);
// Go to next element
cse++; continue;
}
case(StackElem::TYPE_FUNCTION):
{
// Previous element must be OBJECT with type ObjectList. We don't check this, we trust in grammatical parser.
ExecExpr::iterator pr=cse;
pr--;
const ObjectList* arg=dynamic_cast<const ObjectList*>(pr->Object());
const ObjectBase* res;
// Functions check already done, we know that our function exists.
// Sequentally call all functions with given name
auto bounds=G_funcs.equal_range(cse->Name());
std::list<const ObjectError*> errl;
for(auto f=bounds.first; f!=bounds.second; ++f)
{
res=(*(f->second))(arg);
if( !(res==nullptr || res->isError()) ) break;
if(res==nullptr) errl.push_back(new ObjectError("Some function", "unknown reason"));
else errl.push_back(dynamic_cast<const ObjectError*>(res));
}
// Remove previous element
exp.erase(pr);
// Error handling
if( res==nullptr || res->isError() )
{
const struct grammatic_location& loc=cse->Location();
*err=true; // Raise error flag
COUT(ERROR)<<"Can't evaluate function "<<cse->Name()<<" at line "<<loc.first_line<<", position "<<loc.first_column<<std::endl;
for(const auto& inc: loc.incstack) COUT(ERROR)<<" included from "<<inc.filename<<" at line "<<inc.line<<", position "<<inc.column<<std::endl;
for(auto& e: errl)
{
COUT(ERROR)<<e->Function()<<": "<<e->Reason()<<std::endl;
delete e;
}
return nullptr;
}
// Replace function on returned value
cse->ReplaceByObject(res);
// Go to next element
cse++; continue;
}
default: {cse++; continue;} // Other cases leads to undefined behavior (VARIABLE or EMPTY) or doesn't require any action, so, skip.
}
}
// After all, our list contains only one object.
const ObjectBase* ret=exp.begin()->PickObject();
exp.clear();
return ret;
}
void ReplaceVar(ExecExpr& exp, const std::string& var, const ObjectBase* ob)
{
for(auto& se: exp)
if(se.isVar() && var==se.Name())
se.ReplaceByObject(ob->Copy());
}
bool ObjectBase::Save(const char* fname) const bool ObjectBase::Save(const char* fname) const
{ {
size_t size,offset=0,wr; size_t size,offset=0,wr;
@ -15,13 +213,13 @@ bool ObjectBase::Save(const char* fname) const
fd=fopen(fname,"w"); fd=fopen(fname,"w");
serrno=errno; serrno=errno;
if(0==fd) if(nullptr==fd)
{ {
COUT(ERROR)<<"Can't open file "<<fname<<" for writing: "<<strerror(serrno)<<std::endl; COUT(ERROR)<<"Can't open file "<<fname<<" for writing: "<<strerror(serrno)<<std::endl;
return false; return false;
} }
dptr=Blob(&size); dptr=Blob(&size);
if(0==dptr) if(nullptr==dptr)
{ {
COUT(ERROR)<<"Can't get blob for writing to "<<fname<<std::endl; COUT(ERROR)<<"Can't get blob for writing to "<<fname<<std::endl;
fclose(fd); fclose(fd);
@ -43,31 +241,3 @@ bool ObjectBase::Save(const char* fname) const
return true; return true;
} }
ObjectBase* OFunc::Evaluate(bool* err)
{
*err=false;
if(G_funcs.find(name)==G_funcs.end())
{
*err=true;
COUT(ERROR)<<"Function "<<name<< " not found"<<std::endl;
return 0;
}
args->Evaluate(err);
if(*err)
{
COUT(ERROR)<<" in function call "<<Dump()<<std::endl;
return 0;
}
ObjectBase* ret;
auto p=G_funcs.equal_range(name);
for(auto f=p.first;f!=p.second;f++)
{
ret=(*(f->second))(args);
if(ret!=0) return ret;
}
*err=true;
COUT(ERROR)<<"Function "<<name<<" can't evaluate expression "<<args->Dump()<<std::endl;
return 0;
}

103
src/parser/grammatical.y

@ -1,6 +1,4 @@
%language "c" %language "c"
%output "grammatical.cpp"
%defines "../../include/grammatical.h"
%param {yyscan_t scanner} %param {yyscan_t scanner}
%define api.prefix {conf} %define api.prefix {conf}
%define api.pure full %define api.pure full
@ -16,7 +14,6 @@
%{ %{
#include <inttypes.h> #include <inttypes.h>
#include <algorithm>
#include "parser.h" #include "parser.h"
#include "object.h" #include "object.h"
#include "globals.h" #include "globals.h"
@ -34,11 +31,19 @@ inline void conferror(const YYLTYPE* locp, yyscan_t sc, const char* str)
COUT(ERROR)<<std::endl<<" Grammatical parser: "<<str<<std::endl<<" in file "<<locp->filename; COUT(ERROR)<<std::endl<<" Grammatical parser: "<<str<<std::endl<<" in file "<<locp->filename;
if(locp->first_line==locp->last_line) COUT(ERROR)<<" at line "<<locp->last_line<<", from position "<<locp->first_column<<" to "<<locp->last_column<<std::endl; if(locp->first_line==locp->last_line) COUT(ERROR)<<" at line "<<locp->last_line<<", from position "<<locp->first_column<<" to "<<locp->last_column<<std::endl;
else COUT(ERROR)<<" from line "<<locp->first_line<<", position "<<locp->first_column<<" to line "<<locp->last_line<<", position "<<locp->last_column<<std::endl; else COUT(ERROR)<<" from line "<<locp->first_line<<", position "<<locp->first_column<<" to line "<<locp->last_line<<", position "<<locp->last_column<<std::endl;
for(const auto &i:locp->incstack) COUT(ERROR)<<" included from "<<i.filename<<" at line "<<i.line<<", position "<<i.column<<std::endl;
} }
inline void conferror(const YYLTYPE& locp, yyscan_t sc, const std::string& str) inline void conferror(const YYLTYPE& locp, yyscan_t sc, const std::string& str)
{ {
conferror(&locp,sc,str.c_str()); conferror(&locp,sc,str.c_str());
} }
#define EADD(to,from) {(to)->splice((to)->end(),*(from)); delete (from);}
#define EADDV(to,from) {(to).splice((to).end(),*(from)); delete (from);}
#define I(i) {(i)=new ExecExpr;}
#define G_Vars (*(confget_extra(scanner)->vars))
#define G_ToPrint (confget_extra(scanner)->toprint)
#define G_ToSave (confget_extra(scanner)->tosave)
%} %}
%union %union
@ -47,7 +52,7 @@ inline void conferror(const YYLTYPE& locp, yyscan_t sc, const std::string& str)
int64_t i; int64_t i;
double r; double r;
std::string* str; std::string* str;
ObjectBase* ob; ExecExpr* expr;
} }
%token ASSIGN OBRACE CBRACE ENDL DELIM %token ASSIGN OBRACE CBRACE ENDL DELIM
@ -70,11 +75,11 @@ inline void conferror(const YYLTYPE& locp, yyscan_t sc, const std::string& str)
%left '.' %left '.'
%precedence OBRACE CBRACE %precedence OBRACE CBRACE
%type <ob> longidentifier %type <expr> longidentifier
%type <ob> expression %type <expr> expression
%type <ob> object %type <expr> object
%type <ob> pair %type <expr> pair
%type <ob> list %type <expr> list
%destructor {} REAL INTEGER BOOL %destructor {} REAL INTEGER BOOL
%destructor {delete $$;} <*> %destructor {delete $$;} <*>
@ -103,72 +108,72 @@ dirline:
} }
line: line:
NAME ASSIGN object ENDL {COUT(DEBUG)<<" NAME ASSIGN object ENDL\n"; if(G_vars.count(*$1)!=0) delete G_vars[*$1]; G_vars[*$1]=$3; delete $1;} NAME ASSIGN object ENDL {COUT(DEBUG)<<" NAME ASSIGN object ENDL\n"; EADDV(G_Vars[*$1],$3); delete $1;}
| NAME ASSIGN list ENDL {COUT(DEBUG)<<" NAME ASSIGN list ENDL\n"; if(G_vars.count(*$1)!=0) delete G_vars[*$1]; G_vars[*$1]=$3; delete $1;} | NAME ASSIGN list ENDL {COUT(DEBUG)<<" NAME ASSIGN list ENDL\n"; $3->push_front(SEBList(@3)); $3->push_back(SEEList(@4)); EADDV(G_Vars[*$1],$3); delete $1;}
| NAME OBRACE list CBRACE ENDL {COUT(DEBUG)<<" NAME OBRACE list CBRACE ENDL\n"; | NAME OBRACE list CBRACE ENDL {COUT(DEBUG)<<" NAME OBRACE list CBRACE ENDL\n";
transform($1->begin(),$1->end(),$1->begin(),::tolower); tolower($1);
ObjectList* ol=dynamic_cast<ObjectList*>($3); $3->push_front(SEBList(@2));
if(*$1=="save") G_tosave.push_back(ol); $3->push_back(SEEList(@4));
else if(*$1=="print") G_toprint.push_back(ol); if(*$1=="save") {G_ToSave->emplace_back(); EADDV(G_ToSave->back(),$3);}
else if(*$1=="print") {G_ToPrint->emplace_back(); EADDV(G_ToPrint->back(),$3);}
else else
{ {
conferror(@1,scanner,"unknown statement "+(*$1)); conferror(@1,scanner,"unknown statement "+(*$1));
delete ol; delete $1; delete $3;
delete $1;
YYABORT; YYABORT;
} }
delete $1;} delete $1;}
| NAME OBRACE object CBRACE ENDL {COUT(DEBUG)<<" NAME OBRACE object CBRACE ENDL\n"; | NAME OBRACE object CBRACE ENDL {COUT(DEBUG)<<" NAME OBRACE object CBRACE ENDL\n";
transform($1->begin(),$1->end(),$1->begin(),::tolower); tolower($1);
ObjectList* ol=new ObjectList($3); $3->push_front(SEBList(@2));
if(*$1=="save") G_tosave.push_back(ol); $3->push_back(SEEList(@4));
else if(*$1=="print") G_toprint.push_back(ol); if(*$1=="save") {G_ToSave->emplace_back(); EADDV(G_ToSave->back(),$3);}
else if(*$1=="print") {G_ToPrint->emplace_back(); EADDV(G_ToPrint->back(),$3);}
else else
{ {
conferror(@1,scanner,"unknown statement "+(*$1)); conferror(@1,scanner,"unknown statement "+(*$1));
delete ol; delete $1; delete $3;
delete $1;
YYABORT; YYABORT;
} }
delete $1;} delete $1;}
; ;
list: list:
object object {COUT(DEBUG)<<" object object\n"; $$=(new ObjectList($1))->PushBack($2);} object object {COUT(DEBUG)<<" object object\n"; I($$); EADD($$,$1); EADD($$,$2);}
| list object {COUT(DEBUG)<<" list object\n"; dynamic_cast<ObjectList*>($1)->PushBack($2); $$=$1;} | list object {COUT(DEBUG)<<" list object\n"; I($$); EADD($$,$1); EADD($$,$2);}
; ;
pair: pair:
IDENTIFIER ASSIGN object {COUT(DEBUG)<<" IDENTIFIER ASSIGN object\n"; $$=new ObjectPair($1,$3); delete $1;} IDENTIFIER ASSIGN object {COUT(DEBUG)<<" IDENTIFIER ASSIGN object "<<*$1<<"\n"; I($$); EADD($$,$3); $$->push_back(SEMKPair($1,@1)); delete $1;}
; ;
object: object:
STRING {COUT(DEBUG)<<" STRING\n"; $$=new ObjectString($1); delete $1;} STRING {COUT(DEBUG)<<" STRING\n"; I($$); $$->push_back(StackElem(new ObjectString($1),@1)); delete $1;}
| BOOL {COUT(DEBUG)<<" BOOL\n"; $$=new ObjectBool($1);} | BOOL {COUT(DEBUG)<<" BOOL\n"; I($$); $$->push_back(StackElem(new ObjectBool($1),@1));}
| OBRACE list CBRACE {COUT(DEBUG)<<" OBRACE list CBRACE\n"; $$=$2;} | OBRACE list CBRACE {COUT(DEBUG)<<" OBRACE list CBRACE\n"; I($$); $$->push_front(SEBList(@1)); EADD($$,$2); $$->push_back(SEEList(@3));}
| expression %prec ASSIGN {COUT(DEBUG)<<" expression\n"; $$=$1;} | expression %prec ASSIGN {COUT(DEBUG)<<" expression\n"; I($$); EADD($$,$1);}
| pair {COUT(DEBUG)<<" pair\n"; $$=$1;} | pair {COUT(DEBUG)<<" pair\n"; I($$); EADD($$,$1);}
| OBRACE object CBRACE {COUT(DEBUG)<<" OBRACE object CBRACE\n"; $$=$2;} | OBRACE object CBRACE {COUT(DEBUG)<<" OBRACE object CBRACE\n"; I($$); EADD($$,$2);}
| object DELIM {COUT(DEBUG)<<" OBJECT DELIM\n"; $$=$1;} | object DELIM {COUT(DEBUG)<<" OBJECT DELIM\n"; I($$); EADD($$,$1);}
; ;
longidentifier: longidentifier:
IDENTIFIER '.' IDENTIFIER {COUT(DEBUG)<<" IDENTIFIER DOT IDENTIFIER\n"; $$=new OFunc("GET",(new ObjectList(new OId($1)))->PushBack(new ObjectString($3))); delete $1; delete $3;} IDENTIFIER '.' IDENTIFIER {COUT(DEBUG)<<" IDENTIFIER DOT IDENTIFIER\n"; I($$); $$->push_front(SEBList(@1)); $$->push_back(SEVar($1,@1)); $$->push_back(SEObj(new ObjectString($3),@3)); $$->push_back(SEEList(@1)); $$->push_back(SEFunc("GET",@2)); delete $1; delete $3;}
| longidentifier '.' IDENTIFIER {COUT(DEBUG)<<" longidentifier '.' IDENTIFIER\n"; $$=new OFunc("GET",(new ObjectList($1))->PushBack(new ObjectString($3))); delete $3;} | longidentifier '.' IDENTIFIER {COUT(DEBUG)<<" longidentifier '.' IDENTIFIER\n"; I($$); $$->push_front(SEBList(@1)); EADD($$,$1); $$->push_back(SEObj(new ObjectString($3),@3)); $$->push_back(SEEList(@1)); $$->push_back(SEFunc("GET",@2)); delete $3;}
expression: expression:
IDENTIFIER {COUT(DEBUG)<<" IDENTIFIER\n"; $$=new OId($1); delete $1;} IDENTIFIER {COUT(DEBUG)<<" IDENTIFIER\n"; I($$); $$->push_back(SEVar($1,@1)); delete $1;}
| IDENTIFIER OBRACE object CBRACE {COUT(DEBUG)<<" IDENTIFIER OBRACE object CBRACE\n"; $$=new OFunc($1,$3); delete $1;} | IDENTIFIER OBRACE object CBRACE {COUT(DEBUG)<<" IDENTIFIER OBRACE object CBRACE\n"; I($$); $$->push_front(SEBList(@2)); EADD($$,$3); $$->push_back(SEEList(@4)); $$->push_back(SEFunc($1,@1)); delete $1;}
| IDENTIFIER OBRACE list CBRACE {COUT(DEBUG)<<" IDENTIFIER OBRACE list CBRACE\n"; $$=new OFunc($1,$3); delete $1;} | IDENTIFIER OBRACE list CBRACE {COUT(DEBUG)<<" IDENTIFIER OBRACE list CBRACE\n"; I($$); $$->push_front(SEBList(@2)); EADD($$,$3); $$->push_back(SEEList(@4)); $$->push_back(SEFunc($1,@1)); delete $1;}
| longidentifier {COUT(DEBUG)<<" longidentifier\n"; $$=$1;} | longidentifier {COUT(DEBUG)<<" longidentifier\n"; I($$); EADD($$,$1);}
| REAL {COUT(DEBUG)<<" REAL\n"; $$=new ObjectReal($1);} | REAL {COUT(DEBUG)<<" REAL\n"; I($$); $$->push_front(StackElem(new ObjectReal($1),@1));}
| INTEGER {COUT(DEBUG)<<" INTEGER\n"; $$=new ObjectInt($1);} | INTEGER {COUT(DEBUG)<<" INTEGER\n"; I($$); $$->push_front(StackElem(new ObjectInt($1),@1));}
| expression '-' expression {COUT(DEBUG)<<" -\n"; $$=new OFunc("SUB",(new ObjectList($1))->PushBack($3));} | expression '-' expression {COUT(DEBUG)<<" -\n"; I($$); $$->push_front(SEBList(@1)); EADD($$,$1); EADD($$,$3); $$->push_back(SEEList(@3)); $$->push_back(SEFunc("SUB",@2));}
| expression '+' expression {COUT(DEBUG)<<" +\n"; $$=new OFunc("ADD",(new ObjectList($1))->PushBack($3));} | expression '+' expression {COUT(DEBUG)<<" +\n"; I($$); $$->push_front(SEBList(@1)); EADD($$,$1); EADD($$,$3); $$->push_back(SEEList(@3)); $$->push_back(SEFunc("ADD",@2));}
| expression '/' expression {COUT(DEBUG)<<" /\n"; $$=new OFunc("DIV",(new ObjectList($1))->PushBack($3));} | expression '/' expression {COUT(DEBUG)<<" /\n"; I($$); $$->push_front(SEBList(@1)); EADD($$,$1); EADD($$,$3); $$->push_back(SEEList(@3)); $$->push_back(SEFunc("DIV",@2));}
| expression '*' expression {COUT(DEBUG)<<" *\n"; $$=new OFunc("MUL",(new ObjectList($1))->PushBack($3));} | expression '*' expression {COUT(DEBUG)<<" *\n"; I($$); $$->push_front(SEBList(@1)); EADD($$,$1); EADD($$,$3); $$->push_back(SEEList(@3)); $$->push_back(SEFunc("MUL",@2));}
| expression '^' expression {COUT(DEBUG)<<" ^\n"; $$=new OFunc("POW",(new ObjectList($1))->PushBack($3));} | expression '^' expression {COUT(DEBUG)<<" ^\n"; I($$); $$->push_front(SEBList(@1)); EADD($$,$1); EADD($$,$3); $$->push_back(SEEList(@3)); $$->push_back(SEFunc("POW",@2));}
| '-' expression %prec UNARY {COUT(DEBUG)<<" unary -\n"; $$=new OFunc("NEG",$2);} | '-' expression %prec UNARY {COUT(DEBUG)<<" unary -\n"; I($$); $$->push_front(SEBList(@1)); EADD($$,$2); $$->push_back(SEEList(@2)); $$->push_back(SEFunc("NEG",@1));}
| '+' expression %prec UNARY {COUT(DEBUG)<<" unary +\n"; $$=new OFunc("POS",$2);} | '+' expression %prec UNARY {COUT(DEBUG)<<" unary +\n"; I($$); $$->push_front(SEBList(@1)); EADD($$,$2); $$->push_back(SEEList(@2)); $$->push_back(SEFunc("POS",@1));}
| OBRACE expression CBRACE {COUT(DEBUG)<<" OBRACE expression CBRACE\n"; $$=$2;} | OBRACE expression CBRACE {COUT(DEBUG)<<" OBRACE expression CBRACE\n"; I($$); EADD($$,$2);}
; ;

42
src/parser/lexical.l

@ -2,8 +2,6 @@
%option warn %option warn
%option yylineno %option yylineno
%option noyywrap %option noyywrap
%option header-file="../../include/lexical.h"
%option outfile="lexical.cpp"
%option prefix="conf" %option prefix="conf"
%option extra-type="struct lexical_extra*" %option extra-type="struct lexical_extra*"
%option bison-bridge %option bison-bridge
@ -16,23 +14,26 @@
%x PREPROC %x PREPROC
%{ %{
#include <algorithm>
#include "common.h" #include "common.h"
#include "parser.h" #include "parser.h"
#include "object.h"
// flex use register keyword, but it deprecated in C++11. // flex use register keyword, but it deprecated in C++11.
#define register #define register
// Flex always think what bison types prefixed as YY, so we mast define right macroses // Flex always think what bison types prefixed as YY, so we mast define right macroses
#define YYSTYPE CONFSTYPE #define YYSTYPE CONFSTYPE
#define YYLTYPE CONFLTYPE #define YYLTYPE CONFLTYPE
// Some declarations in grammatical.h use ObjectBase
class ObjectBase;
#include "grammatical.h" #include "grammatical.h"
// Get rid of warning on unused function // Get rid of warning on unused function
#define YY_NO_INPUT #define YY_NO_INPUT
// Termination (mostly. in case of errors) // struct yyguts_t defined in each flex output file, this emits warning at linking stage
#define yyguts_t confguts_t
// Termination (mostly, in case of errors)
#define yyterminate(ret) { while(0!=YY_CURRENT_BUFFER) yypop_buffer_state(yyscanner); str.erase(); yyextra->retcode=-(ret); return (ret); } #define yyterminate(ret) { while(0!=YY_CURRENT_BUFFER) yypop_buffer_state(yyscanner); str.erase(); yyextra->retcode=-(ret); return (ret); }
// Abnormal termination with description of error // Abnormal termination with description of error
#define yyerrormessage(message,ret) {COUT(ERROR)<<std::endl<<" Lexical error in file "<<yyextra->state.curdir<<yyextra->state.filename<<" at line "<<yyextra->state.curline<<": "<<(message)<<std::endl; yyterminate(ret);} #define yyerrormessage(message,ret) {\
COUT(ERROR)<<std::endl<<" Lexical error in file "<<yyextra->state.curdir<<yyextra->state.filename<<" at line "<<yyextra->state.curline<<": "<<(message)<<std::endl;\
for(const auto &i:yylloc_param->incstack) COUT(ERROR)<<" included from "<<i.filename<<" at line "<<i.line<<", position "<<i.column<<std::endl;\
yyterminate(ret);}
// Trace position // Trace position
#define yynextline {yyextra->state.curline++; yyextra->state.curpos=0; yyextra->state.curoffset++;} #define yynextline {yyextra->state.curline++; yyextra->state.curpos=0; yyextra->state.curoffset++;}
#define yynextsym {yyextra->state.curpos+=yyleng; yyextra->state.curoffset+=yyleng;} #define yynextsym {yyextra->state.curpos+=yyleng; yyextra->state.curoffset+=yyleng;}
@ -40,13 +41,14 @@ class ObjectBase;
#define setyyllocp {yylloc_param->first_line=yylloc_param->last_line=yyextra->state.curline; yylloc_param->first_column=yyextra->state.curpos; yylloc_param->last_column=yylloc_param->first_column+yyleng;} #define setyyllocp {yylloc_param->first_line=yylloc_param->last_line=yyextra->state.curline; yylloc_param->first_column=yyextra->state.curpos; yylloc_param->last_column=yylloc_param->first_column+yyleng;}
std::string str; std::string str;
int incpos,incline;
%} %}
%% %%
<PARSE,INITIAL>@[Ii][Nn][Cc][Ll][Uu][Dd][Ee][ \t]*\" yy_push_state(INCLUDE,yyscanner); str.erase(); yynextsym; <PARSE,INITIAL>@[Ii][Nn][Cc][Ll][Uu][Dd][Ee][ \t]*\" yy_push_state(INCLUDE,yyscanner); str.erase(); incpos=yyextra->state.curpos; incline=yyextra->state.curline; yynextsym;
@ BEGIN(PREPROC); yynextsym; @ BEGIN(PREPROC); yynextsym;
<PREPROC>[a-zA-Z]* { <PREPROC>[a-zA-Z]* {
yynextsym; str=yytext; transform(str.begin(),str.end(),str.begin(),::tolower); yynextsym; str=yytext; tolower(str);
if("use"==str) return DIR_USE; if("use"==str) return DIR_USE;
if("includepath"==str) return DIR_INCLUDEPATH; if("includepath"==str) return DIR_INCLUDEPATH;
if("modulepath"==str) return DIR_MODULEPATH; if("modulepath"==str) return DIR_MODULEPATH;
@ -85,6 +87,7 @@ std::string str;
} }
if(0==fd) yyerrormessage("can't open file "+str,-1); if(0==fd) yyerrormessage("can't open file "+str,-1);
yylloc_param->incstack.push_front({incline,incpos,yylloc_param->filename});
yynextsym; yynextsym;
yyextra->fds.push(fd); yyextra->fds.push(fd);
yyextra->states.push(yyextra->state); yyextra->states.push(yyextra->state);
@ -103,8 +106,21 @@ std::string str;
} }
[a-zA-Z][a-zA-Z0-9_]* COUT(MOREDEBUG)<<"NAME("<<yytext<<")"; BEGIN(PARSE); yylval_param->str=new std::string(yytext); setyyllocp; yynextsym; return NAME; [a-zA-Z][a-zA-Z0-9_]* COUT(MOREDEBUG)<<"NAME("<<yytext<<")"; BEGIN(PARSE); yylval_param->str=new std::string(yytext); setyyllocp; yynextsym; return NAME;
<PARSE>[+\-*/^] COUT(MOREDEBUG)<<" OPERATION("<<yytext<<")"; setyyllocp; yynextsym; return yytext[0]; <PARSE>[+\-*/^] COUT(MOREDEBUG)<<" OPERATION("<<yytext<<")"; setyyllocp; yynextsym; return yytext[0];
<PARSE>[0-9]+ COUT(MOREDEBUG)<<" INTEGER("<<yytext<<")"; yylval_param->i=atoll(yytext); setyyllocp; yynextsym; return INTEGER; <PARSE>[0-9]+ {
<PARSE>[0-9]+(\.[0-9]*)?([eE][+-][0-9]+)? COUT(MOREDEBUG)<<" REAL("<<yytext<<")"; yylval_param->r=atof(yytext); setyyllocp; yynextsym; return REAL; COUT(MOREDEBUG)<<" INTEGER("<<yytext<<")";
if(!str2int(yytext,&yylval_param->i)) yyerrormessage(std::string("failed conversion to integer of ")+yytext+" at position "+ToString(yyextra->state.curpos),-1);
yylval_param->i=atoll(yytext);
setyyllocp;
yynextsym;
return INTEGER;
}
<PARSE>[0-9]+(\.[0-9]*)?([eE][+-][0-9]+)? {
COUT(MOREDEBUG)<<" REAL("<<yytext<<")";
if(!str2double(yytext,&yylval_param->r)) yyerrormessage(std::string("failed conversion to double of ")+yytext+" at position "+ToString(yyextra->state.curpos),-1);
setyyllocp;
yynextsym;
return REAL;
}
<PARSE>[TF] COUT(MOREDEBUG)<<" BOOL("<<yytext<<")"; yylval_param->b=(yytext[0]=='T')?true:false; setyyllocp; yynextsym; return BOOL; <PARSE>[TF] COUT(MOREDEBUG)<<" BOOL("<<yytext<<")"; yylval_param->b=(yytext[0]=='T')?true:false; setyyllocp; yynextsym; return BOOL;
<PARSE>\( COUT(MOREDEBUG)<<" OBRACE()"; setyyllocp; yynextsym; return OBRACE; <PARSE>\( COUT(MOREDEBUG)<<" OBRACE()"; setyyllocp; yynextsym; return OBRACE;
<PARSE>\) COUT(MOREDEBUG)<<" CBRACE()"; setyyllocp; yynextsym; return CBRACE; <PARSE>\) COUT(MOREDEBUG)<<" CBRACE()"; setyyllocp; yynextsym; return CBRACE;
@ -118,12 +134,12 @@ std::string str;
<PREPROC>\n BEGIN(0); yynextline; <PREPROC>\n BEGIN(0); yynextline;
<PARSE,INITIAL>\#.* yynextsym; <PARSE,INITIAL>\#.* yynextsym;
<PARSE,PREPROC>\" yy_push_state(PSTRING,yyscanner); str.erase(); yyextra->states.push(yyextra->state); yynextsym; <PARSE,PREPROC>\" yy_push_state(PSTRING,yyscanner); str.erase(); yyextra->states.push(yyextra->state); yynextsym;
<PARSE,PREPROC,INITIAL>. yyerrormessage(std::string("unknown symbol ")+yytext+" at position "+std::to_string(yyextra->state.curpos),-1); <PARSE,PREPROC,INITIAL>. yyerrormessage(std::string("unknown symbol ")+yytext+" at position "+ToString(yyextra->state.curpos),-1);
<PSTRING,INCLUDE>\\\\ str+='\\'; yynextsym; <PSTRING,INCLUDE>\\\\ str+='\\'; yynextsym;
<PSTRING,INCLUDE>\\\" str+='\"'; yynextsym; <PSTRING,INCLUDE>\\\" str+='\"'; yynextsym;
<PSTRING>\n str+=yytext[0]; yynextline; <PSTRING>\n str+=yytext[0]; yynextline;
<PSTRING>\" yy_pop_state(yyscanner); COUT(MOREDEBUG)<<" STRING("<<str<<")"; yylval_param->str=new std::string(str); {struct lexical_state prstate=yyextra->states.top(); yyextra->states.pop(); yylloc_param->first_line=prstate.curline; yylloc_param->last_line=yyextra->state.curline; yylloc_param->first_column=prstate.curpos; yylloc_param->last_column=yyextra->state.curpos+yyleng;}; yynextsym; return STRING; <PSTRING>\" yy_pop_state(yyscanner); COUT(MOREDEBUG)<<" STRING("<<str<<")"; yylval_param->str=new std::string(str); {struct lexical_state prstate=yyextra->states.top(); yyextra->states.pop(); yylloc_param->first_line=prstate.curline; yylloc_param->last_line=yyextra->state.curline; yylloc_param->first_column=prstate.curpos; yylloc_param->last_column=yyextra->state.curpos+yyleng;}; yynextsym; return STRING;
<PSTRING,INCLUDE>. str+=yytext[0]; yynextsym; <PSTRING,INCLUDE>. str+=yytext[0]; yynextsym;
<PSTRING,INCLUDE><<EOF>> yyextra->states.pop(); yyerrormessage("unclosed quote",-1); <PSTRING,INCLUDE><<EOF>> yyextra->states.pop(); yyerrormessage("unclosed quote",-1);
<<EOF>> yypop_buffer_state(yyscanner); if(0!=YY_CURRENT_BUFFER) {yyextra->state=yyextra->states.top(); yyextra->states.pop(); fclose(yyextra->fds.top()); yyextra->fds.pop(); yylloc_param->filename=yyextra->state.curdir+yyextra->state.filename;} else yyterminate(0); <<EOF>> yypop_buffer_state(yyscanner); if(0!=YY_CURRENT_BUFFER) {yyextra->state=yyextra->states.top(); yyextra->states.pop(); fclose(yyextra->fds.top()); yyextra->fds.pop(); yylloc_param->filename=yyextra->state.curdir+yyextra->state.filename; yylloc_param->incstack.pop_front();} else yyterminate(0);
%% %%

32
test/Makefile

@ -1,32 +0,0 @@
SHELL=/bin/bash
OPTFLAGS=-O2 -flto -g
EXPORTFLAGS=-fvisibility=hidden -fpic -Wl,--export-dynamic
CPPFLAGS=-std=gnu++11 -I../include -I/home/michael/tmp/gmt5/include/gmt
LIBSFLAGS=-L/home/michael/tmp/gmt5/lib64 -Wl,-rpath /home/michael/tmp/gmt5/lib64 -lgmt -ldl -lpthread $(MODLIBS)
WARNFLAGS=-Wall
CFLAGS=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(CPPFLAGS)
LDFLAGS=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(LIBSFLAGS)
CC=g++
SOURCE = $(wildcard *.cpp)
DEPENDS = $(subst .cpp,.d,$(SOURCE))
OBJECTS = $(subst .cpp,.o,$(SOURCE))
gmttest: $(OBJECTS) $(MODOBJECTS)
$(CC) -o $@ $(OBJECTS) $(MODOBJECTS) $(LDFLAGS)
include $(DEPENDS)
%.o: %.cpp
$(CC) -c $(CFLAGS) -o $@ $<
%.d: %.cpp
$(CC) $(CPPFLAGS) -MM -MT $(subst .cpp,.o,$<) $< | sed 's%\(^.*\):%\1 $@ :%g' >$@
clean:
rm -f *.o *.d
distclean: clean
rm -f gmttest

99
test/gmttest.cpp

@ -1,99 +0,0 @@
#include <gmt.h>
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/wait.h>
int print_func(FILE* fd, const char* str)
{
std::cout<<"PRINT: "<<str<<std::endl;
return 0;
}
struct gmtworkthreadpars
{
void* api;
const char* module;
struct GMT_OPTION* opts;
int fd;
int ret;
};
void gmtonexithandler(int ret, void* x)
{
reinterpret_cast<struct gmtworkthreadpars*>(x)->ret=ret;
close(reinterpret_cast<struct gmtworkthreadpars*>(x)->fd);
pthread_exit(&(reinterpret_cast<struct gmtworkthreadpars*>(x)->ret));
}
void* gmtworkthread(void* x)
{
struct gmtworkthreadpars* p=reinterpret_cast<struct gmtworkthreadpars*>(x);
on_exit(gmtonexithandler,x);
GMT_Append_Option(p->api,GMT_Make_Option(p->api,'>',const_cast<char*>(("/dev/fd/"+std::to_string(p->fd)).c_str())),p->opts);
p->ret=GMT_Call_Module(p->api,p->module,GMT_MODULE_OPT,p->opts);
exit(p->ret);
return 0;
}
int callgmtmodule(void *api, const char *module, struct GMT_OPTION *opts, std::string& res)
{
int pipefd[2];
ssize_t br;
char buffer[4096];
pthread_t wthr;
struct gmtworkthreadpars p;
int *pret;
pipe(pipefd);
p.api=api;
p.module=module;
p.opts=opts;
p.fd=pipefd[1];
pthread_create(&wthr,0,&gmtworkthread,&p);
res.erase();
//read(pipefd[0],buffer,1);
//close(pipefd[1]);
do
{
br=read(pipefd[0],buffer,4096);
std::cout<<res.length()<<" "<<br<<std::endl;
res.append(buffer,br);
} while(0!=br);
close(pipefd[0]);
res.shrink_to_fit();
pthread_join(wthr,reinterpret_cast<void**>(&pret));
return *pret;
}
int main()
{
char* text="GMT";
//void* out;
void* gmtapi;
std::string args="-R130/160/40/60 -JM12c -Xa2c -Ya2c -B5/5/swNE --GMT_HISTORY=f --FONT_ANNOT_PRIMARY=14p,Times-Bold,red";
struct GMT_OPTION* opts;
int io,ret;
char fname[16];
std::string out;
gmtapi=GMT_Create_Session(text,2,3,print_func);
io=GMT_Register_IO(gmtapi,GMT_IS_TEXTSET,GMT_IS_DUPLICATE,GMT_IS_NONE,GMT_OUT,0,0);
GMT_Encode_ID(gmtapi,fname,io);
opts=GMT_Create_Options(gmtapi,0,(void*)args.c_str());
//ret=GMT_Init_IO(gmtapi,GMT_IS_TEXTSET,GMT_IS_NONE,GMT_OUT,5,0,opts);
std::cout<<io<<" "<<GMT_NOTSET<<" "<<fname<<" "<<ret<<std::endl;
ret=callgmtmodule(gmtapi,"psbasemap",opts,out);
std::cout<<"Return value: "<<ret<<std::endl;
std::cout<<out.max_size()<<" "<<out.capacity()<<" "<<out.size()<<std::endl;
GMT_Destroy_Options(gmtapi,&opts);
GMT_Destroy_Session(gmtapi);
return 0;
}

11
tests/GMTCoord

@ -0,0 +1,11 @@
# Description: Coord tests. Must passed.
# Status: ok
# Output hash: 1a1ca4aba24d0fe39407eef250167aca5c21373722f5f2a9bd6495cee34abaf2
@use "gmt"
a=Coord("10:30:18");
b=Coord(10.5);
c=Coord(":10:10");
d=Coord("-0:15:18");
print(a,a.n,b,b.n,c,c.n,d,d.n);

8
tests/GMTCoord_e1

@ -0,0 +1,8 @@
# Description: Non-integer degrees in dd:mm:ss mode.
# Status: fail
# Output hash: 9469953f706df02349e019f8b47a499d169113b654aee46cacb966d01a27848c
@use "gmt"
a=Coord("1.0:10");
print(a);

18
tests/GMTProjection

@ -0,0 +1,18 @@
# Description: Projections test
# Status: ok
# Output hash: 18656de8c59c9a218b71dc730a3a1e8f1016179622392b11f052c64210f6fa2d
@use "gmt"
r=Region("130",150,30,50);
px=Projection("dec",10,(10,20,10,20),15);
p=Projection(r,type="cyl equid",h=10,stpar=0);
p1=Projection(p,type="m",w=10);
p2b=Projection("ob",10,r,"eq",(r.xe.n+r.xb.n)/2,(r.ye.n+r.yb.n)/2,140,15);
p2=Projection(p2b,he=10);
p3=Projection("cas",11,r);
p4=Projection(r,type="cequa",11);
p5=Projection(p4,type="j",centralm="130:30");
p6=Projection("cylstereo",10,r);
print(px,p,p1,p2,p3,p4,p5,p6);

10
tests/GMTRegion1

@ -0,0 +1,10 @@
# Description: Global regions test.
# Status: ok
# Output hash: e661246caef9b2a5fa5a96b9d75c0542d2e0e655de9d63d755aff01cd3b4ceba
@use "gmt"
r1=Region("global");
r2=Region("global360");
r3=Region("global180");
print(r1,r1.xb,r1.xe,r2,r2.xb,r2.xe,r3,r3.xb,r3.xe);

9
tests/GMTRegion2

@ -0,0 +1,9 @@
# Description: Test sequential form of Region.
# Status: ok
# Output hash: a8006abe2c42b9e34c692ab88cf2f869ad6a01d1f32e6da842c1320f0d253d1c
@use "gmt"
r1=Region("10:30",20.5,-10.5,100,"bbox");
r2=Region("10:30",-10.5,20.5,100);
print(r1,r2,r1.yb,r2.yb);

13
tests/GMTRegion3

@ -0,0 +1,13 @@
# Description: Check named pairs form of Region.
# Status: ok
# Output hash: cf4acbeb2da3156fe52f1f374d4dc83a1aa0ee129a8aa2b1975ec828dd23cabe
@use "gmt"
r=Region("10:30",-10.5,20.5,100);
r1=Region(r,type="bbox");
r2=Region(r,type="global180");
r3=Region(r,ye=80.5);
l=(xb=r.xb.n-0.5,(xe=20+r.xb.n-10,yb=-10),(ye="11:30:28"));
rr=Region(l, type="bbox");
print(r,r1,r2,r3,rr);

8
tests/GMTRegion_e1

@ -0,0 +1,8 @@
# Description: Check string argument in sequential form of Region.
# Status: fail
# Output hash: 8421e39ea376a83938c3d79d803749f038ecd493a490228add4b8d179d4e8f75
@use "gmt"
r=Region("10:30",-10.5,20.5,100,"global");
print(r);
Loading…
Cancel
Save